github.com/openconfig/goyang@v1.4.5/pkg/yang/find.go (about)

     1  // Copyright 2015 Google Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package yang
    16  
    17  // This file has functions that search the AST for specified nodes.
    18  
    19  import (
    20  	"reflect"
    21  	"strings"
    22  )
    23  
    24  // localPrefix returns the local prefix used by the containing (sub)module to
    25  // refer to its own module.
    26  func localPrefix(n Node) string {
    27  	return RootNode(n).GetPrefix()
    28  }
    29  
    30  // trimLocalPrefix trims the current module's prefix from the given name. If the
    31  // name is not prefixed with the local module's prefix or is unprefixed
    32  // entirely, then the same string is returned unchanged.
    33  func trimLocalPrefix(n Node, name string) string {
    34  	pfx := localPrefix(n)
    35  	if pfx != "" {
    36  		pfx += ":"
    37  	}
    38  	return strings.TrimPrefix(name, pfx)
    39  }
    40  
    41  // FindGrouping finds the grouping named name according to YANG namespace rules
    42  // using the input node as the initial context node. The seen parameter
    43  // provides a list of the modules previously seen by FindGrouping during
    44  // traversal. If the named grouping cannot be found, nil is returned.
    45  //
    46  // FindGrouping works by recursively looking through the context node's parent
    47  // nodes for grouping fields, or in included or imported submodules/modules for
    48  // externally-defined groupings. Note that any prefix in the name must match
    49  // the module prefix of its import statement in the context node's module.
    50  func FindGrouping(n Node, name string, seen map[string]bool) *Grouping {
    51  	name = trimLocalPrefix(n, name)
    52  	for n != nil {
    53  		// Grab the Grouping field of the underlying structure.  n is
    54  		// always a pointer to a structure,
    55  		e := reflect.ValueOf(n).Elem()
    56  		v := e.FieldByName("Grouping")
    57  		if v.IsValid() {
    58  			for _, g := range v.Interface().([]*Grouping) {
    59  				if g.Name == name {
    60  					return g
    61  				}
    62  			}
    63  		}
    64  		v = e.FieldByName("Import")
    65  		if v.IsValid() {
    66  			for _, i := range v.Interface().([]*Import) {
    67  				// If the prefix matches the import statement,
    68  				// then search for the trimmed name in that module.
    69  				pname := strings.TrimPrefix(name, i.Prefix.Name+":")
    70  				if pname == name {
    71  					continue
    72  				}
    73  				if g := FindGrouping(i.Module, pname, seen); g != nil {
    74  					return g
    75  				}
    76  			}
    77  		}
    78  		v = e.FieldByName("Include")
    79  		if v.IsValid() {
    80  			for _, i := range v.Interface().([]*Include) {
    81  				if seen[i.Module.Name] {
    82  					// Prevent infinite loops in the case that we have already looked at
    83  					// this submodule. This occurs where submodules have include statements
    84  					// in them, or there is a circular dependency.
    85  					continue
    86  				}
    87  				seen[i.Module.Name] = true
    88  				if g := FindGrouping(i.Module, name, seen); g != nil {
    89  					return g
    90  				}
    91  			}
    92  		}
    93  		n = n.ParentNode()
    94  	}
    95  	return nil
    96  }