Spracherkennung für: .go vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]
// Copyright
2024 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version
2.
0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//
http://www.apache.org/licenses/LICENSE-2.
0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package proptools
import (
"fmt"
"reflect"
"slices"
"github.com/google/blueprint/parser"
)
func RepackProperties(props []interface{}) (*parser.Map, error) {
var dereferencedProps []reflect.Value
for _, rawProp := range props {
propStruct := reflect.ValueOf(rawProp)
if !isStructPtr(propStruct.Type()) {
return nil, fmt.Errorf("properties must be *struct, got %s",
propStruct.Type())
}
propStruct = propStruct.Elem()
dereferencedProps = append(dereferencedProps, propStruct)
}
return repackStructs(dereferencedProps)
}
func repackStructs(props []reflect.Value) (*parser.Map, error) {
var allFieldNames []string
for _, prop := range props {
propType := prop.Type()
for i :=
0; i < propType.NumField(); i++ {
field := propType.Field(i)
if !slices.Contains(allFieldNames, field.Name) {
allFieldNames = append(allFieldNames, field.Name)
}
}
}
result := &parser.Map{}
for _, fieldName := range allFieldNames {
var fields []reflect.Value
for _, prop := range props {
field := prop.FieldByName(fieldName)
if field.IsValid() {
fields = append(fields, field)
}
}
if err := assertFieldsEquivalent(fields); err != nil {
return nil, err
}
var expr parser.Expression
var field reflect.Value
for _, f := range fields {
if !isPropEmpty(f) {
field = f
break
}
}
if !field.IsValid() {
continue
}
if isStruct(field.Type()) && !isConfigurable(field.Type()) {
x, err := repackStructs(fields)
if err != nil {
return nil, err
}
if x != nil {
expr = x
}
} else {
x, err := fieldToExpr(field)
if err != nil {
return nil, err
}
if x != nil {
expr = *x
}
}
if expr != nil {
result.Properties = append(result.Properties, &parser.Property{
Name: PropertyNameForField(fieldName),
Value: expr,
})
}
}
return result, nil
}
func fieldToExpr(field reflect.Value) (*parser.Expression, error) {
if IsConfigurable(field.Type()) {
return field.Interface().(configurableReflection).toExpression()
}
if field.Kind() == reflect.Pointer {
if field.IsNil() {
return nil, nil
}
field = field.Elem()
}
switch field.Kind() {
case reflect.String:
var result parser.Expression = &parser.String{Value: field.String()}
return &result, nil
case reflect.Bool:
var result parser.Expression = &parser.Bool{Value: field.Bool()}
return &result, nil
case reflect.Int, reflect.Int64:
var result parser.Expression = &parser.Int64{Value: field.Int()}
return &result, nil
case reflect.Slice:
var contents []parser.Expression
for i :=
0; i < field.Len(); i++ {
inner, err := fieldToExpr(field.Index(i))
if err != nil {
return nil, err
}
contents = append(contents, *inner)
}
var result parser.Expression = &parser.List{Values: contents}
return &result, nil
case reflect.Struct:
var properties []*parser.Property
typ := field.Type()
for i :=
0; i < typ.NumField(); i++ {
inner, err := fieldToExpr(field.Field(i))
if err != nil {
return nil, err
}
properties = append(properties, &parser.Property{
Name: typ.Field(i).Name,
Value: *inner,
})
}
var result parser.Expression = &parser.Map{Properties: properties}
return &result, nil
default:
return nil, fmt.Errorf("Unhandled type: %s", field.Kind().String())
}
}
func isPropEmpty(value reflect.Value) bool {
switch value.Kind() {
case reflect.Pointer:
if value.IsNil() {
return true
}
return isPropEmpty(value.Elem())
case reflect.Struct:
if isConfigurable(value.Type()) {
return value.Interface().(configurableReflection).isEmpty()
}
for i :=
0; i < value.NumField(); i++ {
if !isPropEmpty(value.Field(i)) {
return false
}
}
return true
default:
return false
}
}
func assertFieldsEquivalent(fields []reflect.Value) error {
var firstNonEmpty reflect.Value
var firstIndex int
for i, f := range fields {
if !isPropEmpty(f) {
firstNonEmpty = f
firstIndex = i
break
}
}
if !firstNonEmpty.IsValid() {
return nil
}
for i, f := range fields {
if i != firstIndex && !isPropEmpty(f) {
if err := assertTwoNonEmptyFieldsEquivalent(firstNonEmpty, f); err != nil {
return err
}
}
}
return nil
}
func assertTwoNonEmptyFieldsEquivalent(a, b reflect.Value) error {
aType := a.Type()
bType := b.Type()
if aType != bType {
return fmt.Errorf("fields must have the same type")
}
switch aType.Kind() {
case reflect.Pointer:
return assertTwoNonEmptyFieldsEquivalent(a.Elem(), b.Elem())
case reflect.String:
if a.String() != b.String() {
return fmt.Errorf("Conflicting fields in property structs had values %q and %q", a.String()
, b.String())
}
case reflect.Bool:
if a.Bool() != b.Bool() {
return fmt.Errorf("Conflicting fields in property structs had values %t and %t", a.Bool(), b.Bool())
}
case reflect.Slice:
if a.Len() != b.Len() {
return fmt.Errorf("Conflicting fields in property structs had lengths %d and %d", a.Len(), b.Len())
}
for i := 0; i < a.Len(); i++ {
if err := assertTwoNonEmptyFieldsEquivalent(a.Index(i), b.Index(i)); err != nil {
return err
}
}
case reflect.Int:
if a.Int() != b.Int() {
return fmt.Errorf("Conflicting fields in property structs had values %d and %d", a.Int(), b.Int())
}
case reflect.Struct:
if isConfigurable(a.Type()) {
// We could properly check that two configurables are equivalent, but that's a lot more
// work for a case that I don't think should show up in practice.
return fmt.Errorf("Cannot handle two property structs with nonempty configurable properties")
}
// We don't care about checking if structs are equivalent, we'll check their individual
// fields when we recurse down.
default:
return fmt.Errorf("Unhandled kind: %s", aType.Kind().String())
}
return nil
}