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

     1  // Copyright 2016 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  import (
    18  	"fmt"
    19  	"sort"
    20  	"sync"
    21  )
    22  
    23  // This file implements data structures and functions that relate to the
    24  // identity type.
    25  
    26  // identityDictionary stores a set of identities across all parsed Modules that
    27  // have been resolved to be identified by their module and name.
    28  type identityDictionary struct {
    29  	mu sync.Mutex
    30  	// dict is a global cache of identities keyed by
    31  	// modulename:identityname, where modulename is the full name of the
    32  	// module to which the identity belongs. If the identity were defined
    33  	// in a submodule, then the parent module name is used instead.
    34  	dict map[string]resolvedIdentity
    35  }
    36  
    37  // resolvedIdentity is an Identity that has been disambiguated.
    38  type resolvedIdentity struct {
    39  	Module   *Module
    40  	Identity *Identity
    41  }
    42  
    43  // isEmpty determines whether the resolvedIdentity struct value is populated.
    44  func (r resolvedIdentity) isEmpty() bool {
    45  	return r.Module == nil && r.Identity == nil
    46  }
    47  
    48  // newResolvedIdentity creates a resolved identity from an identity and its
    49  // associated module, and returns the prefixed name (Prefix:IdentityName)
    50  // along with the resolved identity.
    51  func newResolvedIdentity(m *Module, i *Identity) (string, *resolvedIdentity) {
    52  	r := &resolvedIdentity{
    53  		Module:   m,
    54  		Identity: i,
    55  	}
    56  	return i.modulePrefixedName(), r
    57  }
    58  
    59  func appendIfNotIn(ids []*Identity, chk *Identity) []*Identity {
    60  	for _, id := range ids {
    61  		if id == chk {
    62  			return ids
    63  		}
    64  	}
    65  	return append(ids, chk)
    66  }
    67  
    68  // addChildren adds identity r and all of its children to ids
    69  // deterministically.
    70  func addChildren(r *Identity, ids []*Identity) []*Identity {
    71  	ids = appendIfNotIn(ids, r)
    72  
    73  	// Iterate through the values of r.
    74  	for _, ch := range r.Values {
    75  		ids = addChildren(ch, ids)
    76  	}
    77  	return ids
    78  }
    79  
    80  // findIdentityBase returns the resolved identity that is corresponds to the
    81  // baseStr string in the context of the module/submodule mod.
    82  func (mod *Module) findIdentityBase(baseStr string) (*resolvedIdentity, []error) {
    83  	var base resolvedIdentity
    84  	var ok bool
    85  	var errs []error
    86  
    87  	basePrefix, baseName := getPrefix(baseStr)
    88  	rootPrefix := mod.GetPrefix()
    89  	source := Source(mod)
    90  	typeDict := mod.Modules.typeDict
    91  
    92  	switch basePrefix {
    93  	case "", rootPrefix:
    94  		// This is a local identity which is defined within the current
    95  		// module
    96  		keyName := fmt.Sprintf("%s:%s", module(mod).Name, baseName)
    97  		base, ok = typeDict.identities.dict[keyName]
    98  		if !ok {
    99  			errs = append(errs, fmt.Errorf("%s: can't resolve the local base %s as %s", source, baseStr, keyName))
   100  		}
   101  	default:
   102  		// This is an identity which is defined within another module
   103  		extmod := FindModuleByPrefix(mod, basePrefix)
   104  		if extmod == nil {
   105  			errs = append(errs,
   106  				fmt.Errorf("%s: can't find external module with prefix %s", source, basePrefix))
   107  			break
   108  		}
   109  		// The identity we are looking for is modulename:basename.
   110  		if id, ok := typeDict.identities.dict[fmt.Sprintf("%s:%s", module(extmod).Name, baseName)]; ok {
   111  			base = id
   112  			break
   113  		}
   114  
   115  		// Error if we did not find the identity that had the name specified in
   116  		// the module it was expected to be in.
   117  		if base.isEmpty() {
   118  			errs = append(errs, fmt.Errorf("%s: can't resolve remote base %s", source, baseStr))
   119  		}
   120  	}
   121  	return &base, errs
   122  }
   123  
   124  func (ms *Modules) resolveIdentities() []error {
   125  	defer ms.typeDict.identities.mu.Unlock()
   126  	ms.typeDict.identities.mu.Lock()
   127  
   128  	var errs []error
   129  
   130  	// Across all modules, read the identity values that have been extracted
   131  	// from them, and compile them into a "fully resolved" map that means that
   132  	// we can look them up based on the 'real' prefix of the module and the
   133  	// name of the identity.
   134  	for _, mod := range ms.Modules {
   135  		for _, i := range mod.Identities() {
   136  			keyName, r := newResolvedIdentity(mod, i)
   137  			ms.typeDict.identities.dict[keyName] = *r
   138  		}
   139  
   140  		// Hoist up all identities in our included submodules.
   141  		// We could just do a range on ms.SubModules, but that
   142  		// might process a submodule that no module included.
   143  		for _, in := range mod.Include {
   144  			if in.Module == nil {
   145  				continue
   146  			}
   147  			for _, i := range in.Module.Identities() {
   148  				keyName, r := newResolvedIdentity(in.Module, i)
   149  				ms.typeDict.identities.dict[keyName] = *r
   150  			}
   151  		}
   152  	}
   153  
   154  	// Determine which identities have a base statement, and link this to a
   155  	// fully resolved identity statement. The intention here is to make sure
   156  	// that the Children slice is fully populated with pointers to all identities
   157  	// that have a base, so that we can do inheritance of these later.
   158  	for _, i := range ms.typeDict.identities.dict {
   159  		if i.Identity.Base != nil {
   160  			// This identity inherits from one or more other identities.
   161  
   162  			root := RootNode(i.Identity)
   163  			for _, b := range i.Identity.Base {
   164  				base, baseErr := root.findIdentityBase(b.asString())
   165  
   166  				if baseErr != nil {
   167  					errs = append(errs, baseErr...)
   168  					continue
   169  				}
   170  
   171  				// Build up a list of direct children of this identity.
   172  				base.Identity.Values = append(base.Identity.Values, i.Identity)
   173  			}
   174  		}
   175  	}
   176  
   177  	// Do a final sweep through the identities to build up their children.
   178  	for _, i := range ms.typeDict.identities.dict {
   179  		newValues := []*Identity{}
   180  		for _, j := range i.Identity.Values {
   181  			newValues = addChildren(j, newValues)
   182  		}
   183  		sort.SliceStable(newValues, func(j, k int) bool {
   184  			return newValues[j].Name < newValues[k].Name
   185  		})
   186  		i.Identity.Values = newValues
   187  	}
   188  
   189  	return errs
   190  }