1*1fa6dee9SAndroid Build Coastguard Worker// Copyright 2019 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 bpdoc 16*1fa6dee9SAndroid Build Coastguard Worker 17*1fa6dee9SAndroid Build Coastguard Workerimport ( 18*1fa6dee9SAndroid Build Coastguard Worker "fmt" 19*1fa6dee9SAndroid Build Coastguard Worker "go/ast" 20*1fa6dee9SAndroid Build Coastguard Worker "go/doc" 21*1fa6dee9SAndroid Build Coastguard Worker "html/template" 22*1fa6dee9SAndroid Build Coastguard Worker "reflect" 23*1fa6dee9SAndroid Build Coastguard Worker "slices" 24*1fa6dee9SAndroid Build Coastguard Worker "strconv" 25*1fa6dee9SAndroid Build Coastguard Worker "strings" 26*1fa6dee9SAndroid Build Coastguard Worker "unicode" 27*1fa6dee9SAndroid Build Coastguard Worker "unicode/utf8" 28*1fa6dee9SAndroid Build Coastguard Worker 29*1fa6dee9SAndroid Build Coastguard Worker "github.com/google/blueprint/proptools" 30*1fa6dee9SAndroid Build Coastguard Worker) 31*1fa6dee9SAndroid Build Coastguard Worker 32*1fa6dee9SAndroid Build Coastguard Worker// 33*1fa6dee9SAndroid Build Coastguard Worker// Utility functions for PropertyStruct and Property 34*1fa6dee9SAndroid Build Coastguard Worker// 35*1fa6dee9SAndroid Build Coastguard Worker 36*1fa6dee9SAndroid Build Coastguard Workerfunc (ps *PropertyStruct) Clone() *PropertyStruct { 37*1fa6dee9SAndroid Build Coastguard Worker ret := *ps 38*1fa6dee9SAndroid Build Coastguard Worker ret.Properties = slices.Clone(ret.Properties) 39*1fa6dee9SAndroid Build Coastguard Worker for i, prop := range ret.Properties { 40*1fa6dee9SAndroid Build Coastguard Worker ret.Properties[i] = prop.Clone() 41*1fa6dee9SAndroid Build Coastguard Worker } 42*1fa6dee9SAndroid Build Coastguard Worker 43*1fa6dee9SAndroid Build Coastguard Worker return &ret 44*1fa6dee9SAndroid Build Coastguard Worker} 45*1fa6dee9SAndroid Build Coastguard Worker 46*1fa6dee9SAndroid Build Coastguard Workerfunc (p *Property) Clone() Property { 47*1fa6dee9SAndroid Build Coastguard Worker ret := *p 48*1fa6dee9SAndroid Build Coastguard Worker ret.Properties = slices.Clone(ret.Properties) 49*1fa6dee9SAndroid Build Coastguard Worker for i, prop := range ret.Properties { 50*1fa6dee9SAndroid Build Coastguard Worker ret.Properties[i] = prop.Clone() 51*1fa6dee9SAndroid Build Coastguard Worker } 52*1fa6dee9SAndroid Build Coastguard Worker 53*1fa6dee9SAndroid Build Coastguard Worker return ret 54*1fa6dee9SAndroid Build Coastguard Worker} 55*1fa6dee9SAndroid Build Coastguard Worker 56*1fa6dee9SAndroid Build Coastguard Workerfunc (p *Property) Equal(other Property) bool { 57*1fa6dee9SAndroid Build Coastguard Worker return p.Name == other.Name && p.Type == other.Type && p.Tag == other.Tag && 58*1fa6dee9SAndroid Build Coastguard Worker p.Text == other.Text && p.Default == other.Default && 59*1fa6dee9SAndroid Build Coastguard Worker stringArrayEqual(p.OtherNames, other.OtherNames) && 60*1fa6dee9SAndroid Build Coastguard Worker htmlArrayEqual(p.OtherTexts, other.OtherTexts) && 61*1fa6dee9SAndroid Build Coastguard Worker p.SameSubProperties(other) 62*1fa6dee9SAndroid Build Coastguard Worker} 63*1fa6dee9SAndroid Build Coastguard Worker 64*1fa6dee9SAndroid Build Coastguard Workerfunc (ps *PropertyStruct) SetDefaults(defaults reflect.Value) { 65*1fa6dee9SAndroid Build Coastguard Worker setDefaults(ps.Properties, defaults) 66*1fa6dee9SAndroid Build Coastguard Worker} 67*1fa6dee9SAndroid Build Coastguard Worker 68*1fa6dee9SAndroid Build Coastguard Workerfunc setDefaults(properties []Property, defaults reflect.Value) { 69*1fa6dee9SAndroid Build Coastguard Worker for i := range properties { 70*1fa6dee9SAndroid Build Coastguard Worker prop := &properties[i] 71*1fa6dee9SAndroid Build Coastguard Worker fieldName := proptools.FieldNameForProperty(prop.Name) 72*1fa6dee9SAndroid Build Coastguard Worker f := defaults.FieldByName(fieldName) 73*1fa6dee9SAndroid Build Coastguard Worker if (f == reflect.Value{}) { 74*1fa6dee9SAndroid Build Coastguard Worker panic(fmt.Errorf("property %q does not exist in %q", fieldName, defaults.Type())) 75*1fa6dee9SAndroid Build Coastguard Worker } 76*1fa6dee9SAndroid Build Coastguard Worker 77*1fa6dee9SAndroid Build Coastguard Worker if reflect.DeepEqual(f.Interface(), reflect.Zero(f.Type()).Interface()) { 78*1fa6dee9SAndroid Build Coastguard Worker continue 79*1fa6dee9SAndroid Build Coastguard Worker } 80*1fa6dee9SAndroid Build Coastguard Worker 81*1fa6dee9SAndroid Build Coastguard Worker if f.Kind() == reflect.Interface { 82*1fa6dee9SAndroid Build Coastguard Worker f = f.Elem() 83*1fa6dee9SAndroid Build Coastguard Worker } 84*1fa6dee9SAndroid Build Coastguard Worker 85*1fa6dee9SAndroid Build Coastguard Worker if f.Kind() == reflect.Ptr { 86*1fa6dee9SAndroid Build Coastguard Worker if f.IsNil() { 87*1fa6dee9SAndroid Build Coastguard Worker continue 88*1fa6dee9SAndroid Build Coastguard Worker } 89*1fa6dee9SAndroid Build Coastguard Worker f = f.Elem() 90*1fa6dee9SAndroid Build Coastguard Worker } 91*1fa6dee9SAndroid Build Coastguard Worker 92*1fa6dee9SAndroid Build Coastguard Worker if f.Kind() == reflect.Struct { 93*1fa6dee9SAndroid Build Coastguard Worker setDefaults(prop.Properties, f) 94*1fa6dee9SAndroid Build Coastguard Worker } else { 95*1fa6dee9SAndroid Build Coastguard Worker prop.Default = fmt.Sprintf("%v", f.Interface()) 96*1fa6dee9SAndroid Build Coastguard Worker } 97*1fa6dee9SAndroid Build Coastguard Worker } 98*1fa6dee9SAndroid Build Coastguard Worker} 99*1fa6dee9SAndroid Build Coastguard Worker 100*1fa6dee9SAndroid Build Coastguard Workerfunc stringArrayEqual(a, b []string) bool { 101*1fa6dee9SAndroid Build Coastguard Worker if len(a) != len(b) { 102*1fa6dee9SAndroid Build Coastguard Worker return false 103*1fa6dee9SAndroid Build Coastguard Worker } 104*1fa6dee9SAndroid Build Coastguard Worker 105*1fa6dee9SAndroid Build Coastguard Worker for i := range a { 106*1fa6dee9SAndroid Build Coastguard Worker if a[i] != b[i] { 107*1fa6dee9SAndroid Build Coastguard Worker return false 108*1fa6dee9SAndroid Build Coastguard Worker } 109*1fa6dee9SAndroid Build Coastguard Worker } 110*1fa6dee9SAndroid Build Coastguard Worker 111*1fa6dee9SAndroid Build Coastguard Worker return true 112*1fa6dee9SAndroid Build Coastguard Worker} 113*1fa6dee9SAndroid Build Coastguard Worker 114*1fa6dee9SAndroid Build Coastguard Workerfunc htmlArrayEqual(a, b []template.HTML) bool { 115*1fa6dee9SAndroid Build Coastguard Worker if len(a) != len(b) { 116*1fa6dee9SAndroid Build Coastguard Worker return false 117*1fa6dee9SAndroid Build Coastguard Worker } 118*1fa6dee9SAndroid Build Coastguard Worker 119*1fa6dee9SAndroid Build Coastguard Worker for i := range a { 120*1fa6dee9SAndroid Build Coastguard Worker if a[i] != b[i] { 121*1fa6dee9SAndroid Build Coastguard Worker return false 122*1fa6dee9SAndroid Build Coastguard Worker } 123*1fa6dee9SAndroid Build Coastguard Worker } 124*1fa6dee9SAndroid Build Coastguard Worker 125*1fa6dee9SAndroid Build Coastguard Worker return true 126*1fa6dee9SAndroid Build Coastguard Worker} 127*1fa6dee9SAndroid Build Coastguard Worker 128*1fa6dee9SAndroid Build Coastguard Workerfunc (p *Property) SameSubProperties(other Property) bool { 129*1fa6dee9SAndroid Build Coastguard Worker if len(p.Properties) != len(other.Properties) { 130*1fa6dee9SAndroid Build Coastguard Worker return false 131*1fa6dee9SAndroid Build Coastguard Worker } 132*1fa6dee9SAndroid Build Coastguard Worker 133*1fa6dee9SAndroid Build Coastguard Worker for i := range p.Properties { 134*1fa6dee9SAndroid Build Coastguard Worker if !p.Properties[i].Equal(other.Properties[i]) { 135*1fa6dee9SAndroid Build Coastguard Worker return false 136*1fa6dee9SAndroid Build Coastguard Worker } 137*1fa6dee9SAndroid Build Coastguard Worker } 138*1fa6dee9SAndroid Build Coastguard Worker 139*1fa6dee9SAndroid Build Coastguard Worker return true 140*1fa6dee9SAndroid Build Coastguard Worker} 141*1fa6dee9SAndroid Build Coastguard Worker 142*1fa6dee9SAndroid Build Coastguard Workerfunc (ps *PropertyStruct) GetByName(name string) *Property { 143*1fa6dee9SAndroid Build Coastguard Worker return getByName(name, "", &ps.Properties) 144*1fa6dee9SAndroid Build Coastguard Worker} 145*1fa6dee9SAndroid Build Coastguard Worker 146*1fa6dee9SAndroid Build Coastguard Workerfunc (ps *PropertyStruct) Nest(nested *PropertyStruct) { 147*1fa6dee9SAndroid Build Coastguard Worker ps.Properties = nestUnique(ps.Properties, nested.Properties) 148*1fa6dee9SAndroid Build Coastguard Worker} 149*1fa6dee9SAndroid Build Coastguard Worker 150*1fa6dee9SAndroid Build Coastguard Worker// Adds a target element to src if it does not exist in src 151*1fa6dee9SAndroid Build Coastguard Workerfunc nestUnique(src []Property, target []Property) []Property { 152*1fa6dee9SAndroid Build Coastguard Worker var ret []Property 153*1fa6dee9SAndroid Build Coastguard Worker ret = append(ret, src...) 154*1fa6dee9SAndroid Build Coastguard Worker for _, elem := range target { 155*1fa6dee9SAndroid Build Coastguard Worker isUnique := true 156*1fa6dee9SAndroid Build Coastguard Worker for _, retElement := range ret { 157*1fa6dee9SAndroid Build Coastguard Worker if elem.Equal(retElement) { 158*1fa6dee9SAndroid Build Coastguard Worker isUnique = false 159*1fa6dee9SAndroid Build Coastguard Worker break 160*1fa6dee9SAndroid Build Coastguard Worker } 161*1fa6dee9SAndroid Build Coastguard Worker } 162*1fa6dee9SAndroid Build Coastguard Worker if isUnique { 163*1fa6dee9SAndroid Build Coastguard Worker ret = append(ret, elem) 164*1fa6dee9SAndroid Build Coastguard Worker } 165*1fa6dee9SAndroid Build Coastguard Worker } 166*1fa6dee9SAndroid Build Coastguard Worker return ret 167*1fa6dee9SAndroid Build Coastguard Worker} 168*1fa6dee9SAndroid Build Coastguard Worker 169*1fa6dee9SAndroid Build Coastguard Workerfunc getByName(name string, prefix string, props *[]Property) *Property { 170*1fa6dee9SAndroid Build Coastguard Worker for i := range *props { 171*1fa6dee9SAndroid Build Coastguard Worker if prefix+(*props)[i].Name == name { 172*1fa6dee9SAndroid Build Coastguard Worker return &(*props)[i] 173*1fa6dee9SAndroid Build Coastguard Worker } else if strings.HasPrefix(name, prefix+(*props)[i].Name+".") { 174*1fa6dee9SAndroid Build Coastguard Worker return getByName(name, prefix+(*props)[i].Name+".", &(*props)[i].Properties) 175*1fa6dee9SAndroid Build Coastguard Worker } 176*1fa6dee9SAndroid Build Coastguard Worker } 177*1fa6dee9SAndroid Build Coastguard Worker return nil 178*1fa6dee9SAndroid Build Coastguard Worker} 179*1fa6dee9SAndroid Build Coastguard Worker 180*1fa6dee9SAndroid Build Coastguard Workerfunc (p *Property) Nest(nested *PropertyStruct) { 181*1fa6dee9SAndroid Build Coastguard Worker p.Properties = nestUnique(p.Properties, nested.Properties) 182*1fa6dee9SAndroid Build Coastguard Worker} 183*1fa6dee9SAndroid Build Coastguard Worker 184*1fa6dee9SAndroid Build Coastguard Workerfunc (p *Property) SetAnonymous() { 185*1fa6dee9SAndroid Build Coastguard Worker p.Anonymous = true 186*1fa6dee9SAndroid Build Coastguard Worker} 187*1fa6dee9SAndroid Build Coastguard Worker 188*1fa6dee9SAndroid Build Coastguard Workerfunc newPropertyStruct(t *doc.Type) (*PropertyStruct, error) { 189*1fa6dee9SAndroid Build Coastguard Worker typeSpec := t.Decl.Specs[0].(*ast.TypeSpec) 190*1fa6dee9SAndroid Build Coastguard Worker ps := PropertyStruct{ 191*1fa6dee9SAndroid Build Coastguard Worker Name: t.Name, 192*1fa6dee9SAndroid Build Coastguard Worker Text: t.Doc, 193*1fa6dee9SAndroid Build Coastguard Worker } 194*1fa6dee9SAndroid Build Coastguard Worker 195*1fa6dee9SAndroid Build Coastguard Worker structType, ok := typeSpec.Type.(*ast.StructType) 196*1fa6dee9SAndroid Build Coastguard Worker if !ok { 197*1fa6dee9SAndroid Build Coastguard Worker return nil, fmt.Errorf("type of %q is not a struct", t.Name) 198*1fa6dee9SAndroid Build Coastguard Worker } 199*1fa6dee9SAndroid Build Coastguard Worker 200*1fa6dee9SAndroid Build Coastguard Worker var err error 201*1fa6dee9SAndroid Build Coastguard Worker ps.Properties, err = structProperties(structType) 202*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 203*1fa6dee9SAndroid Build Coastguard Worker return nil, err 204*1fa6dee9SAndroid Build Coastguard Worker } 205*1fa6dee9SAndroid Build Coastguard Worker 206*1fa6dee9SAndroid Build Coastguard Worker return &ps, nil 207*1fa6dee9SAndroid Build Coastguard Worker} 208*1fa6dee9SAndroid Build Coastguard Worker 209*1fa6dee9SAndroid Build Coastguard Workerfunc structProperties(structType *ast.StructType) (props []Property, err error) { 210*1fa6dee9SAndroid Build Coastguard Worker for _, f := range structType.Fields.List { 211*1fa6dee9SAndroid Build Coastguard Worker names := f.Names 212*1fa6dee9SAndroid Build Coastguard Worker if names == nil { 213*1fa6dee9SAndroid Build Coastguard Worker // Anonymous fields have no name, use the type as the name 214*1fa6dee9SAndroid Build Coastguard Worker // TODO: hide the name and make the properties show up in the embedding struct 215*1fa6dee9SAndroid Build Coastguard Worker if t, ok := f.Type.(*ast.Ident); ok { 216*1fa6dee9SAndroid Build Coastguard Worker names = append(names, t) 217*1fa6dee9SAndroid Build Coastguard Worker } 218*1fa6dee9SAndroid Build Coastguard Worker } 219*1fa6dee9SAndroid Build Coastguard Worker for _, n := range names { 220*1fa6dee9SAndroid Build Coastguard Worker var name, tag, text string 221*1fa6dee9SAndroid Build Coastguard Worker if n != nil { 222*1fa6dee9SAndroid Build Coastguard Worker name = proptools.PropertyNameForField(n.Name) 223*1fa6dee9SAndroid Build Coastguard Worker } 224*1fa6dee9SAndroid Build Coastguard Worker if f.Doc != nil { 225*1fa6dee9SAndroid Build Coastguard Worker text = f.Doc.Text() 226*1fa6dee9SAndroid Build Coastguard Worker } 227*1fa6dee9SAndroid Build Coastguard Worker if f.Tag != nil { 228*1fa6dee9SAndroid Build Coastguard Worker tag, err = strconv.Unquote(f.Tag.Value) 229*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 230*1fa6dee9SAndroid Build Coastguard Worker return nil, err 231*1fa6dee9SAndroid Build Coastguard Worker } 232*1fa6dee9SAndroid Build Coastguard Worker } 233*1fa6dee9SAndroid Build Coastguard Worker typ, innerProps, err := getType(f.Type) 234*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 235*1fa6dee9SAndroid Build Coastguard Worker return nil, err 236*1fa6dee9SAndroid Build Coastguard Worker } 237*1fa6dee9SAndroid Build Coastguard Worker 238*1fa6dee9SAndroid Build Coastguard Worker props = append(props, Property{ 239*1fa6dee9SAndroid Build Coastguard Worker Name: name, 240*1fa6dee9SAndroid Build Coastguard Worker Type: typ, 241*1fa6dee9SAndroid Build Coastguard Worker Tag: reflect.StructTag(tag), 242*1fa6dee9SAndroid Build Coastguard Worker Text: formatText(text), 243*1fa6dee9SAndroid Build Coastguard Worker Properties: innerProps, 244*1fa6dee9SAndroid Build Coastguard Worker }) 245*1fa6dee9SAndroid Build Coastguard Worker } 246*1fa6dee9SAndroid Build Coastguard Worker } 247*1fa6dee9SAndroid Build Coastguard Worker 248*1fa6dee9SAndroid Build Coastguard Worker return props, nil 249*1fa6dee9SAndroid Build Coastguard Worker} 250*1fa6dee9SAndroid Build Coastguard Worker 251*1fa6dee9SAndroid Build Coastguard Workerfunc getType(expr ast.Expr) (typ string, innerProps []Property, err error) { 252*1fa6dee9SAndroid Build Coastguard Worker var t ast.Expr 253*1fa6dee9SAndroid Build Coastguard Worker if star, ok := expr.(*ast.StarExpr); ok { 254*1fa6dee9SAndroid Build Coastguard Worker t = star.X 255*1fa6dee9SAndroid Build Coastguard Worker } else { 256*1fa6dee9SAndroid Build Coastguard Worker t = expr 257*1fa6dee9SAndroid Build Coastguard Worker } 258*1fa6dee9SAndroid Build Coastguard Worker switch a := t.(type) { 259*1fa6dee9SAndroid Build Coastguard Worker case *ast.ArrayType: 260*1fa6dee9SAndroid Build Coastguard Worker var elt string 261*1fa6dee9SAndroid Build Coastguard Worker elt, innerProps, err = getType(a.Elt) 262*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 263*1fa6dee9SAndroid Build Coastguard Worker return "", nil, err 264*1fa6dee9SAndroid Build Coastguard Worker } 265*1fa6dee9SAndroid Build Coastguard Worker typ = "list of " + elt 266*1fa6dee9SAndroid Build Coastguard Worker case *ast.InterfaceType: 267*1fa6dee9SAndroid Build Coastguard Worker typ = "interface" 268*1fa6dee9SAndroid Build Coastguard Worker case *ast.Ident: 269*1fa6dee9SAndroid Build Coastguard Worker typ = a.Name 270*1fa6dee9SAndroid Build Coastguard Worker case *ast.StructType: 271*1fa6dee9SAndroid Build Coastguard Worker innerProps, err = structProperties(a) 272*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 273*1fa6dee9SAndroid Build Coastguard Worker return "", nil, err 274*1fa6dee9SAndroid Build Coastguard Worker } 275*1fa6dee9SAndroid Build Coastguard Worker case *ast.IndexExpr: 276*1fa6dee9SAndroid Build Coastguard Worker // IndexExpr is used to represent generic type arguments 277*1fa6dee9SAndroid Build Coastguard Worker if !isConfigurableAst(a.X) { 278*1fa6dee9SAndroid Build Coastguard Worker var writer strings.Builder 279*1fa6dee9SAndroid Build Coastguard Worker if err := ast.Fprint(&writer, nil, expr, nil); err != nil { 280*1fa6dee9SAndroid Build Coastguard Worker return "", nil, err 281*1fa6dee9SAndroid Build Coastguard Worker } 282*1fa6dee9SAndroid Build Coastguard Worker return "", nil, fmt.Errorf("unknown type %s", writer.String()) 283*1fa6dee9SAndroid Build Coastguard Worker } 284*1fa6dee9SAndroid Build Coastguard Worker var innerType string 285*1fa6dee9SAndroid Build Coastguard Worker innerType, innerProps, err = getType(a.Index) 286*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 287*1fa6dee9SAndroid Build Coastguard Worker return "", nil, err 288*1fa6dee9SAndroid Build Coastguard Worker } 289*1fa6dee9SAndroid Build Coastguard Worker typ = "configurable " + innerType 290*1fa6dee9SAndroid Build Coastguard Worker default: 291*1fa6dee9SAndroid Build Coastguard Worker typ = fmt.Sprintf("%T", expr) 292*1fa6dee9SAndroid Build Coastguard Worker } 293*1fa6dee9SAndroid Build Coastguard Worker 294*1fa6dee9SAndroid Build Coastguard Worker return typ, innerProps, nil 295*1fa6dee9SAndroid Build Coastguard Worker} 296*1fa6dee9SAndroid Build Coastguard Worker 297*1fa6dee9SAndroid Build Coastguard Workerfunc isConfigurableAst(expr ast.Expr) bool { 298*1fa6dee9SAndroid Build Coastguard Worker switch e := expr.(type) { 299*1fa6dee9SAndroid Build Coastguard Worker case *ast.Ident: 300*1fa6dee9SAndroid Build Coastguard Worker return e.Name == "Configurable" 301*1fa6dee9SAndroid Build Coastguard Worker case *ast.SelectorExpr: 302*1fa6dee9SAndroid Build Coastguard Worker if l, ok := e.X.(*ast.Ident); ok && l.Name == "proptools" { 303*1fa6dee9SAndroid Build Coastguard Worker if e.Sel.Name == "Configurable" { 304*1fa6dee9SAndroid Build Coastguard Worker return true 305*1fa6dee9SAndroid Build Coastguard Worker } 306*1fa6dee9SAndroid Build Coastguard Worker } 307*1fa6dee9SAndroid Build Coastguard Worker } 308*1fa6dee9SAndroid Build Coastguard Worker return false 309*1fa6dee9SAndroid Build Coastguard Worker} 310*1fa6dee9SAndroid Build Coastguard Worker 311*1fa6dee9SAndroid Build Coastguard Workerfunc (ps *PropertyStruct) ExcludeByTag(key, value string) { 312*1fa6dee9SAndroid Build Coastguard Worker filterPropsByTag(&ps.Properties, key, value, true) 313*1fa6dee9SAndroid Build Coastguard Worker} 314*1fa6dee9SAndroid Build Coastguard Worker 315*1fa6dee9SAndroid Build Coastguard Workerfunc (ps *PropertyStruct) IncludeByTag(key, value string) { 316*1fa6dee9SAndroid Build Coastguard Worker filterPropsByTag(&ps.Properties, key, value, false) 317*1fa6dee9SAndroid Build Coastguard Worker} 318*1fa6dee9SAndroid Build Coastguard Worker 319*1fa6dee9SAndroid Build Coastguard Workerfunc filterPropsByTag(props *[]Property, key, value string, exclude bool) { 320*1fa6dee9SAndroid Build Coastguard Worker // Create a slice that shares the storage of props but has 0 length. Appending up to 321*1fa6dee9SAndroid Build Coastguard Worker // len(props) times to this slice will overwrite the original slice contents 322*1fa6dee9SAndroid Build Coastguard Worker filtered := (*props)[:0] 323*1fa6dee9SAndroid Build Coastguard Worker for _, x := range *props { 324*1fa6dee9SAndroid Build Coastguard Worker if hasTag(x.Tag, key, value) == !exclude { 325*1fa6dee9SAndroid Build Coastguard Worker filterPropsByTag(&x.Properties, key, value, exclude) 326*1fa6dee9SAndroid Build Coastguard Worker filtered = append(filtered, x) 327*1fa6dee9SAndroid Build Coastguard Worker } 328*1fa6dee9SAndroid Build Coastguard Worker } 329*1fa6dee9SAndroid Build Coastguard Worker 330*1fa6dee9SAndroid Build Coastguard Worker *props = filtered 331*1fa6dee9SAndroid Build Coastguard Worker} 332*1fa6dee9SAndroid Build Coastguard Worker 333*1fa6dee9SAndroid Build Coastguard Workerfunc hasTag(tag reflect.StructTag, key, value string) bool { 334*1fa6dee9SAndroid Build Coastguard Worker for _, entry := range strings.Split(tag.Get(key), ",") { 335*1fa6dee9SAndroid Build Coastguard Worker if entry == value { 336*1fa6dee9SAndroid Build Coastguard Worker return true 337*1fa6dee9SAndroid Build Coastguard Worker } 338*1fa6dee9SAndroid Build Coastguard Worker } 339*1fa6dee9SAndroid Build Coastguard Worker return false 340*1fa6dee9SAndroid Build Coastguard Worker} 341*1fa6dee9SAndroid Build Coastguard Worker 342*1fa6dee9SAndroid Build Coastguard Workerfunc formatText(text string) template.HTML { 343*1fa6dee9SAndroid Build Coastguard Worker var html template.HTML 344*1fa6dee9SAndroid Build Coastguard Worker lines := strings.Split(text, "\n") 345*1fa6dee9SAndroid Build Coastguard Worker preformatted := false 346*1fa6dee9SAndroid Build Coastguard Worker for _, line := range lines { 347*1fa6dee9SAndroid Build Coastguard Worker r, _ := utf8.DecodeRuneInString(line) 348*1fa6dee9SAndroid Build Coastguard Worker indent := unicode.IsSpace(r) 349*1fa6dee9SAndroid Build Coastguard Worker if indent && !preformatted { 350*1fa6dee9SAndroid Build Coastguard Worker html += "<pre>\n\n" 351*1fa6dee9SAndroid Build Coastguard Worker preformatted = true 352*1fa6dee9SAndroid Build Coastguard Worker } else if !indent && line != "" && preformatted { 353*1fa6dee9SAndroid Build Coastguard Worker html += "</pre>\n" 354*1fa6dee9SAndroid Build Coastguard Worker preformatted = false 355*1fa6dee9SAndroid Build Coastguard Worker } 356*1fa6dee9SAndroid Build Coastguard Worker html += template.HTML(template.HTMLEscapeString(line)) + "\n" 357*1fa6dee9SAndroid Build Coastguard Worker } 358*1fa6dee9SAndroid Build Coastguard Worker if preformatted { 359*1fa6dee9SAndroid Build Coastguard Worker html += "</pre>\n" 360*1fa6dee9SAndroid Build Coastguard Worker } 361*1fa6dee9SAndroid Build Coastguard Worker return html 362*1fa6dee9SAndroid Build Coastguard Worker} 363