Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 1 | package bpdoc |
| 2 | |
| 3 | import ( |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 4 | "fmt" |
Colin Cross | 4ef23af | 2017-10-17 17:40:42 -0700 | [diff] [blame] | 5 | "html/template" |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 6 | "reflect" |
| 7 | "sort" |
Liz Kammer | 5c9fe38 | 2020-09-25 12:49:21 -0700 | [diff] [blame] | 8 | "strings" |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 9 | |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 10 | "github.com/google/blueprint/proptools" |
| 11 | ) |
| 12 | |
Jaewoong Jung | 781f6b2 | 2019-02-06 16:20:17 -0800 | [diff] [blame] | 13 | // Package contains the information about a package relevant to generating documentation. |
| 14 | type Package struct { |
| 15 | // Name is the name of the package. |
| 16 | Name string |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 17 | |
Jaewoong Jung | 781f6b2 | 2019-02-06 16:20:17 -0800 | [diff] [blame] | 18 | // Path is the full package path of the package as used in the primary builder. |
| 19 | Path string |
| 20 | |
| 21 | // Text is the contents of the package comment documenting the module types in the package. |
| 22 | Text string |
| 23 | |
| 24 | // ModuleTypes is a list of ModuleType objects that contain information about each module type that is |
| 25 | // defined by the package. |
| 26 | ModuleTypes []*ModuleType |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 27 | } |
| 28 | |
Jaewoong Jung | 781f6b2 | 2019-02-06 16:20:17 -0800 | [diff] [blame] | 29 | // ModuleType contains the information about a module type that is relevant to generating documentation. |
| 30 | type ModuleType struct { |
| 31 | // Name is the string that will appear in Blueprints files when defining a new module of |
| 32 | // this type. |
| 33 | Name string |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 34 | |
Jaewoong Jung | 781f6b2 | 2019-02-06 16:20:17 -0800 | [diff] [blame] | 35 | // PkgPath is the full package path of the package that contains the module type factory. |
| 36 | PkgPath string |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 37 | |
Jaewoong Jung | 781f6b2 | 2019-02-06 16:20:17 -0800 | [diff] [blame] | 38 | // Text is the contents of the comment documenting the module type. |
Jaewoong Jung | 8bc6bf1 | 2019-03-11 10:54:36 -0700 | [diff] [blame] | 39 | Text template.HTML |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 40 | |
Jaewoong Jung | 781f6b2 | 2019-02-06 16:20:17 -0800 | [diff] [blame] | 41 | // PropertyStructs is a list of PropertyStruct objects that contain information about each |
| 42 | // property struct that is used by the module type, containing all properties that are valid |
| 43 | // for the module type. |
| 44 | PropertyStructs []*PropertyStruct |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 45 | } |
| 46 | |
Colin Cross | d9f6fd5 | 2016-05-31 16:16:00 -0700 | [diff] [blame] | 47 | type PropertyStruct struct { |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 48 | Name string |
| 49 | Text string |
Colin Cross | d9f6fd5 | 2016-05-31 16:16:00 -0700 | [diff] [blame] | 50 | Properties []Property |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 51 | } |
| 52 | |
Colin Cross | d9f6fd5 | 2016-05-31 16:16:00 -0700 | [diff] [blame] | 53 | type Property struct { |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 54 | Name string |
| 55 | OtherNames []string |
| 56 | Type string |
| 57 | Tag reflect.StructTag |
Colin Cross | 7311337 | 2017-10-17 18:01:04 -0700 | [diff] [blame] | 58 | Text template.HTML |
| 59 | OtherTexts []template.HTML |
Colin Cross | d9f6fd5 | 2016-05-31 16:16:00 -0700 | [diff] [blame] | 60 | Properties []Property |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 61 | Default string |
Liz Kammer | 5c9fe38 | 2020-09-25 12:49:21 -0700 | [diff] [blame] | 62 | Anonymous bool |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 63 | } |
| 64 | |
Jaewoong Jung | 781f6b2 | 2019-02-06 16:20:17 -0800 | [diff] [blame] | 65 | func AllPackages(pkgFiles map[string][]string, moduleTypeNameFactories map[string]reflect.Value, |
| 66 | moduleTypeNamePropertyStructs map[string][]interface{}) ([]*Package, error) { |
| 67 | // Read basic info from the files to construct a Reader instance. |
| 68 | r := NewReader(pkgFiles) |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 69 | |
Jaewoong Jung | 781f6b2 | 2019-02-06 16:20:17 -0800 | [diff] [blame] | 70 | pkgMap := map[string]*Package{} |
| 71 | var pkgs []*Package |
| 72 | // Scan through per-module-type property structs map. |
| 73 | for mtName, propertyStructs := range moduleTypeNamePropertyStructs { |
| 74 | // Construct ModuleType with the given info. |
| 75 | mtInfo, err := assembleModuleTypeInfo(r, mtName, moduleTypeNameFactories[mtName], propertyStructs) |
| 76 | if err != nil { |
| 77 | return nil, err |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 78 | } |
Jaewoong Jung | 781f6b2 | 2019-02-06 16:20:17 -0800 | [diff] [blame] | 79 | // Some pruning work |
Liz Kammer | 5c9fe38 | 2020-09-25 12:49:21 -0700 | [diff] [blame] | 80 | removeAnonymousProperties(mtInfo) |
Jaewoong Jung | 781f6b2 | 2019-02-06 16:20:17 -0800 | [diff] [blame] | 81 | removeEmptyPropertyStructs(mtInfo) |
| 82 | collapseDuplicatePropertyStructs(mtInfo) |
| 83 | collapseNestedPropertyStructs(mtInfo) |
| 84 | combineDuplicateProperties(mtInfo) |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 85 | |
Jaewoong Jung | 781f6b2 | 2019-02-06 16:20:17 -0800 | [diff] [blame] | 86 | // Add the ModuleInfo to the corresponding Package map/slice entries. |
| 87 | pkg := pkgMap[mtInfo.PkgPath] |
| 88 | if pkg == nil { |
| 89 | var err error |
| 90 | pkg, err = r.Package(mtInfo.PkgPath) |
| 91 | if err != nil { |
| 92 | return nil, err |
Colin Cross | c3d7312 | 2016-08-05 17:19:36 -0700 | [diff] [blame] | 93 | } |
Jaewoong Jung | 781f6b2 | 2019-02-06 16:20:17 -0800 | [diff] [blame] | 94 | pkgMap[mtInfo.PkgPath] = pkg |
| 95 | pkgs = append(pkgs, pkg) |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 96 | } |
Jaewoong Jung | 781f6b2 | 2019-02-06 16:20:17 -0800 | [diff] [blame] | 97 | pkg.ModuleTypes = append(pkg.ModuleTypes, mtInfo) |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 98 | } |
Jaewoong Jung | 781f6b2 | 2019-02-06 16:20:17 -0800 | [diff] [blame] | 99 | |
| 100 | // Sort ModuleTypes within each package. |
| 101 | for _, pkg := range pkgs { |
| 102 | sort.Slice(pkg.ModuleTypes, func(i, j int) bool { return pkg.ModuleTypes[i].Name < pkg.ModuleTypes[j].Name }) |
| 103 | } |
| 104 | // Sort packages. |
| 105 | sort.Slice(pkgs, func(i, j int) bool { return pkgs[i].Path < pkgs[j].Path }) |
| 106 | |
| 107 | return pkgs, nil |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 108 | } |
| 109 | |
Jaewoong Jung | 781f6b2 | 2019-02-06 16:20:17 -0800 | [diff] [blame] | 110 | func assembleModuleTypeInfo(r *Reader, name string, factory reflect.Value, |
| 111 | propertyStructs []interface{}) (*ModuleType, error) { |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 112 | |
Jaewoong Jung | 781f6b2 | 2019-02-06 16:20:17 -0800 | [diff] [blame] | 113 | mt, err := r.ModuleType(name, factory) |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 114 | if err != nil { |
| 115 | return nil, err |
| 116 | } |
| 117 | |
Jaewoong Jung | 781f6b2 | 2019-02-06 16:20:17 -0800 | [diff] [blame] | 118 | // Reader.ModuleType only fills basic information such as name and package path. Collect more info |
| 119 | // from property struct data. |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 120 | for _, s := range propertyStructs { |
| 121 | v := reflect.ValueOf(s).Elem() |
| 122 | t := v.Type() |
| 123 | |
| 124 | // Ignore property structs with unexported or unnamed types |
| 125 | if t.PkgPath() == "" { |
| 126 | continue |
| 127 | } |
Jaewoong Jung | 781f6b2 | 2019-02-06 16:20:17 -0800 | [diff] [blame] | 128 | ps, err := r.PropertyStruct(t.PkgPath(), t.Name(), v) |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 129 | if err != nil { |
| 130 | return nil, err |
| 131 | } |
Colin Cross | d9f6fd5 | 2016-05-31 16:16:00 -0700 | [diff] [blame] | 132 | ps.ExcludeByTag("blueprint", "mutated") |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 133 | |
Liz Kammer | 5c9fe38 | 2020-09-25 12:49:21 -0700 | [diff] [blame] | 134 | for _, nestedProperty := range nestedPropertyStructs(v) { |
| 135 | nestedName := nestedProperty.nestPoint |
| 136 | nestedValue := nestedProperty.value |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 137 | nestedType := nestedValue.Type() |
| 138 | |
| 139 | // Ignore property structs with unexported or unnamed types |
| 140 | if nestedType.PkgPath() == "" { |
| 141 | continue |
| 142 | } |
Jaewoong Jung | 781f6b2 | 2019-02-06 16:20:17 -0800 | [diff] [blame] | 143 | nested, err := r.PropertyStruct(nestedType.PkgPath(), nestedType.Name(), nestedValue) |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 144 | if err != nil { |
| 145 | return nil, err |
| 146 | } |
Colin Cross | d9f6fd5 | 2016-05-31 16:16:00 -0700 | [diff] [blame] | 147 | nested.ExcludeByTag("blueprint", "mutated") |
Liz Kammer | 5c9fe38 | 2020-09-25 12:49:21 -0700 | [diff] [blame] | 148 | if nestedName == "" { |
| 149 | ps.Nest(nested) |
| 150 | } else { |
| 151 | nestPoint := ps.GetByName(nestedName) |
| 152 | if nestPoint == nil { |
| 153 | return nil, fmt.Errorf("nesting point %q not found", nestedName) |
| 154 | } |
| 155 | nestPoint.Nest(nested) |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 156 | } |
| 157 | |
Liz Kammer | 5c9fe38 | 2020-09-25 12:49:21 -0700 | [diff] [blame] | 158 | if nestedProperty.anonymous { |
| 159 | if nestedName != "" { |
| 160 | nestedName += "." |
| 161 | } |
| 162 | nestedName += proptools.PropertyNameForField(nested.Name) |
| 163 | nestedProp := ps.GetByName(nestedName) |
Liz Kammer | 2068e08 | 2020-10-05 14:47:47 -0700 | [diff] [blame] | 164 | // Anonymous properties may have already been omitted, no need to ensure they are filtered later |
| 165 | if nestedProp != nil { |
| 166 | // Set property to anonymous to allow future filtering |
| 167 | nestedProp.SetAnonymous() |
Liz Kammer | 5c9fe38 | 2020-09-25 12:49:21 -0700 | [diff] [blame] | 168 | } |
Liz Kammer | 5c9fe38 | 2020-09-25 12:49:21 -0700 | [diff] [blame] | 169 | } |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 170 | } |
Colin Cross | d9f6fd5 | 2016-05-31 16:16:00 -0700 | [diff] [blame] | 171 | mt.PropertyStructs = append(mt.PropertyStructs, ps) |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 172 | } |
| 173 | |
Colin Cross | d9f6fd5 | 2016-05-31 16:16:00 -0700 | [diff] [blame] | 174 | return mt, nil |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 175 | } |
| 176 | |
Liz Kammer | 5c9fe38 | 2020-09-25 12:49:21 -0700 | [diff] [blame] | 177 | type nestedProperty struct { |
| 178 | nestPoint string |
| 179 | value reflect.Value |
| 180 | anonymous bool |
| 181 | } |
| 182 | |
| 183 | func nestedPropertyStructs(s reflect.Value) []nestedProperty { |
| 184 | ret := make([]nestedProperty, 0) |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 185 | var walk func(structValue reflect.Value, prefix string) |
| 186 | walk = func(structValue reflect.Value, prefix string) { |
Liz Kammer | 5c9fe38 | 2020-09-25 12:49:21 -0700 | [diff] [blame] | 187 | var nestStruct func(field reflect.StructField, value reflect.Value, fieldName string) |
| 188 | nestStruct = func(field reflect.StructField, value reflect.Value, fieldName string) { |
| 189 | nestPoint := prefix |
| 190 | if field.Anonymous { |
| 191 | nestPoint = strings.TrimSuffix(nestPoint, ".") |
| 192 | } else { |
| 193 | nestPoint = nestPoint + proptools.PropertyNameForField(fieldName) |
| 194 | } |
| 195 | ret = append(ret, nestedProperty{nestPoint: nestPoint, value: value, anonymous: field.Anonymous}) |
| 196 | if nestPoint != "" { |
| 197 | nestPoint += "." |
| 198 | } |
| 199 | walk(value, nestPoint) |
| 200 | } |
| 201 | |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 202 | typ := structValue.Type() |
| 203 | for i := 0; i < structValue.NumField(); i++ { |
| 204 | field := typ.Field(i) |
| 205 | if field.PkgPath != "" { |
| 206 | // The field is not exported so just skip it. |
| 207 | continue |
| 208 | } |
Jaewoong Jung | bd0f6c3 | 2019-05-28 13:16:20 -0700 | [diff] [blame] | 209 | if proptools.HasTag(field, "blueprint", "mutated") { |
| 210 | continue |
| 211 | } |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 212 | |
| 213 | fieldValue := structValue.Field(i) |
| 214 | |
| 215 | switch fieldValue.Kind() { |
| 216 | case reflect.Bool, reflect.String, reflect.Slice, reflect.Int, reflect.Uint: |
| 217 | // Nothing |
| 218 | case reflect.Struct: |
Liz Kammer | 5c9fe38 | 2020-09-25 12:49:21 -0700 | [diff] [blame] | 219 | nestStruct(field, fieldValue, field.Name) |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 220 | case reflect.Ptr, reflect.Interface: |
Liz Kammer | 5c9fe38 | 2020-09-25 12:49:21 -0700 | [diff] [blame] | 221 | |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 222 | if !fieldValue.IsNil() { |
| 223 | // We leave the pointer intact and zero out the struct that's |
| 224 | // pointed to. |
| 225 | elem := fieldValue.Elem() |
| 226 | if fieldValue.Kind() == reflect.Interface { |
| 227 | if elem.Kind() != reflect.Ptr { |
| 228 | panic(fmt.Errorf("can't get type of field %q: interface "+ |
| 229 | "refers to a non-pointer", field.Name)) |
| 230 | } |
| 231 | elem = elem.Elem() |
| 232 | } |
Dan Willemsen | 9c4e050 | 2016-01-13 13:56:24 -0800 | [diff] [blame] | 233 | if elem.Kind() == reflect.Struct { |
Liz Kammer | 5c9fe38 | 2020-09-25 12:49:21 -0700 | [diff] [blame] | 234 | nestStruct(field, elem, field.Name) |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 235 | } |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 236 | } |
| 237 | default: |
| 238 | panic(fmt.Errorf("unexpected kind for property struct field %q: %s", |
| 239 | field.Name, fieldValue.Kind())) |
| 240 | } |
| 241 | } |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 242 | } |
| 243 | |
| 244 | walk(s, "") |
| 245 | return ret |
| 246 | } |
| 247 | |
| 248 | // Remove any property structs that have no exported fields |
Colin Cross | 92639f3 | 2017-12-11 14:39:41 -0800 | [diff] [blame] | 249 | func removeEmptyPropertyStructs(mt *ModuleType) { |
Colin Cross | d9f6fd5 | 2016-05-31 16:16:00 -0700 | [diff] [blame] | 250 | for i := 0; i < len(mt.PropertyStructs); i++ { |
| 251 | if len(mt.PropertyStructs[i].Properties) == 0 { |
| 252 | mt.PropertyStructs = append(mt.PropertyStructs[:i], mt.PropertyStructs[i+1:]...) |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 253 | i-- |
| 254 | } |
| 255 | } |
| 256 | } |
| 257 | |
Liz Kammer | 5c9fe38 | 2020-09-25 12:49:21 -0700 | [diff] [blame] | 258 | // Remove any property structs that are anonymous |
| 259 | func removeAnonymousProperties(mt *ModuleType) { |
| 260 | var removeAnonymousProps func(props []Property) []Property |
| 261 | removeAnonymousProps = func(props []Property) []Property { |
| 262 | newProps := make([]Property, 0, len(props)) |
| 263 | for _, p := range props { |
| 264 | if p.Anonymous { |
| 265 | continue |
| 266 | } |
| 267 | if len(p.Properties) > 0 { |
| 268 | p.Properties = removeAnonymousProps(p.Properties) |
| 269 | } |
| 270 | newProps = append(newProps, p) |
| 271 | } |
| 272 | return newProps |
| 273 | } |
| 274 | for _, ps := range mt.PropertyStructs { |
| 275 | ps.Properties = removeAnonymousProps(ps.Properties) |
| 276 | } |
| 277 | } |
| 278 | |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 279 | // Squashes duplicates of the same property struct into single entries |
Colin Cross | 92639f3 | 2017-12-11 14:39:41 -0800 | [diff] [blame] | 280 | func collapseDuplicatePropertyStructs(mt *ModuleType) { |
Colin Cross | d9f6fd5 | 2016-05-31 16:16:00 -0700 | [diff] [blame] | 281 | var collapsed []*PropertyStruct |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 282 | |
| 283 | propertyStructLoop: |
Colin Cross | d9f6fd5 | 2016-05-31 16:16:00 -0700 | [diff] [blame] | 284 | for _, from := range mt.PropertyStructs { |
| 285 | for _, to := range collapsed { |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 286 | if from.Name == to.Name { |
Sasha Smundak | 797563b | 2019-02-14 10:58:48 -0800 | [diff] [blame] | 287 | CollapseDuplicateProperties(&to.Properties, &from.Properties) |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 288 | continue propertyStructLoop |
| 289 | } |
| 290 | } |
Colin Cross | d9f6fd5 | 2016-05-31 16:16:00 -0700 | [diff] [blame] | 291 | collapsed = append(collapsed, from) |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 292 | } |
Colin Cross | d9f6fd5 | 2016-05-31 16:16:00 -0700 | [diff] [blame] | 293 | mt.PropertyStructs = collapsed |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 294 | } |
| 295 | |
Sasha Smundak | 797563b | 2019-02-14 10:58:48 -0800 | [diff] [blame] | 296 | func CollapseDuplicateProperties(to, from *[]Property) { |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 297 | propertyLoop: |
| 298 | for _, f := range *from { |
| 299 | for i := range *to { |
| 300 | t := &(*to)[i] |
| 301 | if f.Name == t.Name { |
Sasha Smundak | 797563b | 2019-02-14 10:58:48 -0800 | [diff] [blame] | 302 | CollapseDuplicateProperties(&t.Properties, &f.Properties) |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 303 | continue propertyLoop |
| 304 | } |
| 305 | } |
| 306 | *to = append(*to, f) |
| 307 | } |
| 308 | } |
| 309 | |
| 310 | // Find all property structs that only contain structs, and move their children up one with |
| 311 | // a prefixed name |
Colin Cross | 92639f3 | 2017-12-11 14:39:41 -0800 | [diff] [blame] | 312 | func collapseNestedPropertyStructs(mt *ModuleType) { |
Colin Cross | d9f6fd5 | 2016-05-31 16:16:00 -0700 | [diff] [blame] | 313 | for _, ps := range mt.PropertyStructs { |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 314 | collapseNestedProperties(&ps.Properties) |
| 315 | } |
| 316 | } |
| 317 | |
Colin Cross | d9f6fd5 | 2016-05-31 16:16:00 -0700 | [diff] [blame] | 318 | func collapseNestedProperties(p *[]Property) { |
| 319 | var n []Property |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 320 | |
| 321 | for _, parent := range *p { |
| 322 | var containsProperty bool |
| 323 | for j := range parent.Properties { |
| 324 | child := &parent.Properties[j] |
| 325 | if len(child.Properties) > 0 { |
| 326 | collapseNestedProperties(&child.Properties) |
| 327 | } else { |
| 328 | containsProperty = true |
| 329 | } |
| 330 | } |
| 331 | if containsProperty || len(parent.Properties) == 0 { |
| 332 | n = append(n, parent) |
| 333 | } else { |
| 334 | for j := range parent.Properties { |
| 335 | child := parent.Properties[j] |
| 336 | child.Name = parent.Name + "." + child.Name |
| 337 | n = append(n, child) |
| 338 | } |
| 339 | } |
| 340 | } |
| 341 | *p = n |
| 342 | } |
| 343 | |
Colin Cross | 92639f3 | 2017-12-11 14:39:41 -0800 | [diff] [blame] | 344 | func combineDuplicateProperties(mt *ModuleType) { |
Colin Cross | d9f6fd5 | 2016-05-31 16:16:00 -0700 | [diff] [blame] | 345 | for _, ps := range mt.PropertyStructs { |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 346 | combineDuplicateSubProperties(&ps.Properties) |
| 347 | } |
| 348 | } |
| 349 | |
Colin Cross | d9f6fd5 | 2016-05-31 16:16:00 -0700 | [diff] [blame] | 350 | func combineDuplicateSubProperties(p *[]Property) { |
| 351 | var n []Property |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 352 | propertyLoop: |
| 353 | for _, child := range *p { |
| 354 | if len(child.Properties) > 0 { |
| 355 | combineDuplicateSubProperties(&child.Properties) |
| 356 | for i := range n { |
| 357 | s := &n[i] |
| 358 | if s.SameSubProperties(child) { |
| 359 | s.OtherNames = append(s.OtherNames, child.Name) |
| 360 | s.OtherTexts = append(s.OtherTexts, child.Text) |
| 361 | continue propertyLoop |
| 362 | } |
| 363 | } |
| 364 | } |
| 365 | n = append(n, child) |
| 366 | } |
Colin Cross | 4572edd | 2015-05-13 14:36:24 -0700 | [diff] [blame] | 367 | *p = n |
| 368 | } |