Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  bpmodify.go   Sprache: unbekannt

 
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 bpmodify

import (
 "bytes"
 "errors"
 "fmt"
 "slices"
 "strings"

 "github.com/google/blueprint/parser"
)

// NewBlueprint returns a Blueprint for the given file contents that allows making modifications.
func NewBlueprint(filename string, data []byte) (*Blueprint, error) {
 r := bytes.NewReader(data)
 file, errs := parser.Parse(filename, r)
 if len(errs) > 0 {
  return nil, errors.Join(errs...)
 }

 return &Blueprint{
  data:   data,
  bpFile: file,
 }, nil
}

type Blueprint struct {
 data     []byte
 bpFile   *parser.File
 modified bool
}

// Bytes returns a copy of the current, possibly modified contents of the Blueprint as a byte slice.
func (bp *Blueprint) Bytes() ([]byte, error) {
 if bp.modified {
  data, err := parser.Print(bp.bpFile)
  if err != nil {
   return nil, err
  }
  return data, nil
 }
 return slices.Clone(bp.data), nil
}

// String returns the current, possibly modified contents of the Blueprint as a string.
func (bp *Blueprint) String() string {
 data, err := bp.Bytes()
 if err != nil {
  return err.Error()
 }
 return string(data)
}

// Modified returns true if any of the calls on the Blueprint caused the contents to be modified.
func (bp *Blueprint) Modified() bool {
 return bp.modified
}

// ModulesByName returns a ModuleSet that contains all modules with the given list of names.
// Requesting a module that does not exist is not an error.
func (bp *Blueprint) ModulesByName(names ...string) *ModuleSet {
 moduleSet := &ModuleSet{
  bp: bp,
 }
 for _, def := range bp.bpFile.Defs {
  module, ok := def.(*parser.Module)
  if !ok {
   continue
  }

  for _, prop := range module.Properties {
   if prop.Name == "name" {
    if stringValue, ok := prop.Value.(*parser.String); ok && slices.Contains(names, stringValue.Value) {
     moduleSet.modules = append(moduleSet.modules, module)
    }
   }
  }
 }

 return moduleSet
}

// AllModules returns a ModuleSet that contains all modules in the Blueprint.
func (bp *Blueprint) AllModules() *ModuleSet {
 moduleSet := &ModuleSet{
  bp: bp,
 }
 for _, def := range bp.bpFile.Defs {
  module, ok := def.(*parser.Module)
  if !ok {
   continue
  }

  moduleSet.modules = append(moduleSet.modules, module)
 }

 return moduleSet
}

// A ModuleSet represents a set of modules in a Blueprint, and can be used to make modifications
// the modules.
type ModuleSet struct {
 bp      *Blueprint
 modules []*parser.Module
}

// GetProperty returns a PropertySet that contains all properties with the given list of names
// in all modules in the ModuleSet.  Requesting properties that do not exist is not an error.
// It returns an error for a malformed property name, or if the requested property is nested
// in a property that is not a map.
func (ms *ModuleSet) GetProperty(properties ...string) (*PropertySet, error) {
 propertySet := &PropertySet{
  bp: ms.bp,
 }

 targetProperties, err := parseQualifiedProperties(properties)
 if err != nil {
  return nil, err
 }

 for _, targetProperty := range targetProperties {
  for _, module := range ms.modules {
   prop, _, err := getRecursiveProperty(module, targetProperty)
   if err != nil {
    return nil, err
   } else if prop == nil {
    continue
   }
   propertySet.properties = append(propertySet.properties, &property{
    property: prop,
    module:   module,
    name:     targetProperty,
   })
  }
 }

 return propertySet, nil
}

// GetOrCreateProperty returns a PropertySet that contains all properties with the given list of names
// in all modules in the ModuleSet, creating empty placeholder properties if they don't exist.
// It returns an error for a malformed property name, or if the requested property is nested
// in a property that is not a map.
func (ms *ModuleSet) GetOrCreateProperty(typ Type, properties ...string) (*PropertySet, error) {
 propertySet := &PropertySet{
  bp: ms.bp,
 }

 targetProperties, err := parseQualifiedProperties(properties)
 if err != nil {
  return nil, err
 }

 for _, targetProperty := range targetProperties {
  for _, module := range ms.modules {
   prop, _, err := getRecursiveProperty(module, targetProperty)
   if err != nil {
    return nil, err
   } else if prop == nil {
    prop, err = createRecursiveProperty(module, targetProperty, parser.ZeroExpression(parser.Type(typ)))
    if err != nil {
     return nil, err
    }
    ms.bp.modified = true
   } else {
    if prop.Value.Type() != parser.Type(typ) {
     return nil, fmt.Errorf("unexpected type found in property %q, wanted %s, found %s",
      targetProperty.String(), typ, prop.Value.Type())
    }
   }
   propertySet.properties = append(propertySet.properties, &property{
    property: prop,
    module:   module,
    name:     targetProperty,
   })
  }
 }

 return propertySet, nil
}

// RemoveProperty removes the given list of properties from all modules in the ModuleSet.
// It returns an error for a malformed property name, or if the requested property is nested
// in a property that is not a map.  Removing a property that does not exist is not an error.
func (ms *ModuleSet) RemoveProperty(properties ...string) error {
 targetProperties, err := parseQualifiedProperties(properties)
 if err != nil {
  return err
 }

 for _, targetProperty := range targetProperties {
  for _, module := range ms.modules {
   prop, parent, err := getRecursiveProperty(module, targetProperty)
   if err != nil {
    return err
   } else if prop != nil {
    parent.RemoveProperty(prop.Name)
    ms.bp.modified = true
   }
  }
 }
 return nil
}

// MoveProperty moves the given list of properties to a new parent property.
// It returns an error for a malformed property name, or if the requested property is nested
// in a property that is not a map.  Moving a property that does not exist is not an error.
func (ms *ModuleSet) MoveProperty(newParent string, properties ...string) error {
 targetProperties, err := parseQualifiedProperties(properties)
 if err != nil {
  return err
 }

 for _, targetProperty := range targetProperties {
  for _, module := range ms.modules {
   prop, parent, err := getRecursiveProperty(module, targetProperty)
   if err != nil {
    return err
   } else if prop != nil {
    parent.MovePropertyContents(prop.Name, newParent)
    ms.bp.modified = true
   }
  }
 }
 return nil
}

// PropertySet represents a set of properties in a set of modules.
type PropertySet struct {
 bp         *Blueprint
 properties []*property
 sortLists  bool
}

type property struct {
 property *parser.Property
 module   *parser.Module
 name     *qualifiedProperty
}

// SortListsWhenModifying causes any future modifications to lists in the PropertySet to sort
// the lists.  Otherwise, lists are only sorted if they appear to be sorted before modification.
func (ps *PropertySet) SortListsWhenModifying() {
 ps.sortLists = true
}

// SetString sets all properties in the PropertySet to the given string.  It returns an error
// if any of the properties are not strings.
func (ps *PropertySet) SetString(s string) error {
 var errs []error
 for _, prop := range ps.properties {
  value := prop.property.Value
  str, ok := value.(*parser.String)
  if !ok {
   errs = append(errs, fmt.Errorf("expected property %s in module %s to be string, found %s",
    prop.name, prop.module.Name(), value.Type().String()))
   continue
  }
  if str.Value != s {
   str.Value = s
   ps.bp.modified = true
  }
 }

 return errors.Join(errs...)
}

// SetBool sets all properties in the PropertySet to the given boolean.  It returns an error
// if any of the properties are not booleans.
func (ps *PropertySet) SetBool(b bool) error {
 var errs []error
 for _, prop := range ps.properties {
  value := prop.property.Value
  res, ok := value.(*parser.Bool)
  if !ok {
   errs = append(errs, fmt.Errorf("expected property %s in module %s to be bool, found %s",
    prop.name, prop.module.Name(), value.Type().String()))
   continue
  }
  if res.Value != b {
   res.Value = b
   ps.bp.modified = true
  }
 }
 return errors.Join(errs...)
}

// AddStringToList adds the given strings to all properties in the PropertySet.  It returns an error
// if any of the properties are not lists of strings.
func (ps *PropertySet) AddStringToList(strs ...string) error {
 var errs []error
 for _, prop := range ps.properties {
  value := prop.property.Value
  list, ok := value.(*parser.List)
  if !ok {
   errs = append(errs, fmt.Errorf("expected property %s in module %s to be list, found %s",
    prop.name, prop.module.Name(), value.Type()))
   continue
  }
  wasSorted := parser.ListIsSorted(list)
  modified := false
  for _, s := range strs {
   m := parser.AddStringToList(list, s)
   modified = modified || m
  }
  if modified {
   ps.bp.modified = true
   if wasSorted || ps.sortLists {
    parser.SortList(ps.bp.bpFile, list)
   }
  }
 }

 return errors.Join(errs...)
}

// RemoveStringFromList removes the given strings to all properties in the PropertySet if they are present.
// It returns an error  if any of the properties are not lists of strings.
func (ps *PropertySet) RemoveStringFromList(strs ...string) error {
 var errs []error
 for _, prop := range ps.properties {
  value := prop.property.Value
  list, ok := value.(*parser.List)
  if !ok {
   errs = append(errs, fmt.Errorf("expected property %s in module %s to be list, found %s",
    prop.name, prop.module.Name(), value.Type()))
   continue
  }
  wasSorted := parser.ListIsSorted(list)
  modified := false
  for _, s := range strs {
   m := parser.RemoveStringFromList(list, s)
   modified = modified || m
  }
  if modified {
   ps.bp.modified = true
   if wasSorted || ps.sortLists {
    parser.SortList(ps.bp.bpFile, list)
   }
  }
 }

 return errors.Join(errs...)
}

// AddLiteral adds the given literal blueprint snippet to all properties in the PropertySet if they are present.
// It returns an error  if any of the properties are not lists.
func (ps *PropertySet) AddLiteral(s string) error {
 var errs []error
 for _, prop := range ps.properties {
  value := prop.property.Value
  if ps.sortLists {
   return fmt.Errorf("sorting not supported when adding a literal")
  }
  list, ok := value.(*parser.List)
  if !ok {
   errs = append(errs, fmt.Errorf("expected property %s in module %s to be list, found %s",
    prop.name, prop.module.Name(), value.Type().String()))
   continue
  }
  value, parseErrs := parser.ParseExpression(strings.NewReader(s))
  if len(parseErrs) > 0 {
   errs = append(errs, parseErrs...)
   continue
  }
  list.Values = append(list.Values, value)
  ps.bp.modified = true
 }

 return errors.Join(errs...)
}

// ReplaceStrings applies replacements to all properties in the PropertySet.  It replaces all instances
// of the strings in the keys of the given map with their corresponding values.  It returns an error
// if any of the properties are not lists of strings.
func (ps *PropertySet) ReplaceStrings(replacements map[string]string) error {
 var errs []error
 for _, prop := range ps.properties {
  value := prop.property.Value
  if list, ok := value.(*parser.List); ok {
   modified := parser.ReplaceStringsInList(list, replacements)
   if modified {
    ps.bp.modified = true
   }
  } else if str, ok := value.(*parser.String); ok {
   oldVal := str.Value
   replacementValue := replacements[oldVal]
   if replacementValue != "" {
    str.Value = replacementValue
    ps.bp.modified = true
   }
  } else {
   errs = append(errs, fmt.Errorf("expected property %s in module %s to be a list or string, found %s",
    prop.name, prop.module.Name(), value.Type().String()))
  }
 }
 return errors.Join(errs...)
}

func getRecursiveProperty(module *parser.Module, property *qualifiedProperty) (prop *parser.Property,
 parent *parser.Map, err error) {

 parent, err = traverseToQualifiedPropertyParent(module, property, false)
 if err != nil {
  return nil, nil, err
 }
 if parent == nil {
  return nil, nil, nil
 }
 if prop, found := parent.GetProperty(property.name()); found {
  return prop, parent, nil
 }

 return nil, nil, nil
}

func createRecursiveProperty(module *parser.Module, property *qualifiedProperty,
 value parser.Expression) (prop *parser.Property, err error) {
 parent, err := traverseToQualifiedPropertyParent(module, property, true)
 if err != nil {
  return nil, err
 }
 if _, found := parent.GetProperty(property.name()); found {
  return nil, fmt.Errorf("property %q already exists", property.String())
 }

 prop = &parser.Property{Name: property.name(), Value: value}
 parent.Properties = append(parent.Properties, prop)
 return prop, nil
}

func traverseToQualifiedPropertyParent(module *parser.Module, property *qualifiedProperty,
 create bool) (parent *parser.Map, err error) {
 m := &module.Map
 for i, prefix := range property.prefixes() {
  if prop, found := m.GetProperty(prefix); found {
   if mm, ok := prop.Value.(*parser.Map); ok {
    m = mm
   } else {
    // We've found a property in the AST and such property is not of type *parser.Map
    return nil, fmt.Errorf("Expected property %q to be a map, found %s",
     strings.Join(property.prefixes()[:i+1], "."), prop.Value.Type())
   }
  } else if create {
   mm := &parser.Map{}
   m.Properties = append(m.Properties, &parser.Property{Name: prefix, Value: mm})
   m = mm
  } else {
   return nil, nil
  }
 }
 return m, nil
}

type qualifiedProperty struct {
 parts []string
}

func (p *qualifiedProperty) name() string {
 return p.parts[len(p.parts)-1]
}
func (p *qualifiedProperty) prefixes() []string {
 return p.parts[:len(p.parts)-1]
}
func (p *qualifiedProperty) String() string {
 return strings.Join(p.parts, ".")
}

func parseQualifiedProperty(s string) (*qualifiedProperty, error) {
 parts := strings.Split(s, ".")
 if len(parts) == 0 {
  return nil, fmt.Errorf("%q is not a valid property name", s)
 }
 for _, part := range parts {
  if part == "" {
   return nil, fmt.Errorf("%q is not a valid property name", s)
  }
 }
 prop := qualifiedProperty{parts}
 return &prop, nil

}

func parseQualifiedProperties(properties []string) ([]*qualifiedProperty, error) {
 var qualifiedProperties []*qualifiedProperty
 var errs []error
 for _, property := range properties {
  qualifiedProperty, err := parseQualifiedProperty(property)
  if err != nil {
   errs = append(errs, err)
  }
  qualifiedProperties = append(qualifiedProperties, qualifiedProperty)
 }
 if len(errs) > 0 {
  return nil, errors.Join(errs...)
 }
 return qualifiedProperties, nil
}

type Type parser.Type

var (
 List   = Type(parser.ListType)
 String = Type(parser.StringType)
 Bool   = Type(parser.BoolType)
 Int64  = Type(parser.Int64Type)
 Map    = Type(parser.MapType)
)

func (t Type) String() string {
 return parser.Type(t).String()
}

[Dauer der Verarbeitung: 0.23 Sekunden, vorverarbeitet 2026-06-28]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik