Spracherkennung für: .go vermutete Sprache: Abap {ABAP[60] C[36] CS[36]} [Methode: maximale Elemente, drei 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
}