class Puppet::Pops::Types::PTypeSetType

Constants

DEFAULT
TYPE_STRING_OR_RANGE
TYPE_STRING_OR_VERSION
TYPE_TYPESET_I12N
TYPE_TYPE_REFERENCE_I12N

Attributes

annotations[R]
name[R]
name_authority[R]
pcore_uri[R]
pcore_version[R]
references[R]
types[R]
version[R]

Public Class Methods

new(name_or_init_hash, init_hash_expression = nil, name_authority = nil) click to toggle source

Initialize a TypeSet Type instance. The initialization will use either a name and an initialization hash expression, or a fully resolved initialization hash.

@overload initialize(name, init_hash_expression)

Used when the TypeSet type is loaded using a type alias expression. When that happens, it is important that
the actual resolution of the expression is deferred until all definitions have been made known to the current
loader. The package will then be resolved when it is loaded by the {TypeParser}. "resolved" here, means that
the hash expression is fully resolved, and then passed to the {#_pcore_init_from_hash} method.
@param name [String] The name of the type set
@param init_hash_expression [Model::LiteralHash] The hash describing the TypeSet features
@param name_authority [String] The default name authority for the type set

@overload initialize(init_hash)

Used when the package is created by the {TypeFactory}. The init_hash must be fully resolved.
@param init_hash [Hash{String=>Object}] The hash describing the TypeSet features

@api private

    # File lib/puppet/pops/types/p_type_set_type.rb
 91 def initialize(name_or_init_hash, init_hash_expression = nil, name_authority = nil)
 92   @types = EMPTY_HASH
 93   @references = EMPTY_HASH
 94 
 95   if name_or_init_hash.is_a?(Hash)
 96     _pcore_init_from_hash(name_or_init_hash)
 97   else
 98     # Creation using "type XXX = TypeSet[{}]". This means that the name is given
 99     @name = TypeAsserter.assert_instance_of('TypeSet name', Pcore::TYPE_QUALIFIED_REFERENCE, name_or_init_hash)
100     @name_authority = TypeAsserter.assert_instance_of('TypeSet name_authority', Pcore::TYPE_URI, name_authority, true)
101     @init_hash_expression = init_hash_expression
102   end
103 end
register_ptype(loader, ir) click to toggle source
   # File lib/puppet/pops/types/p_type_set_type.rb
61 def self.register_ptype(loader, ir)
62   create_ptype(loader, ir, 'AnyType', '_pcore_init_hash' => TYPE_TYPESET_I12N.resolve(loader))
63 end

Public Instance Methods

[](qname) click to toggle source

Resolve a type in this type set using a qualified name. The resolved type may either be a type defined in this type set or a type defined in a type set that is referenced by this type set (nesting may occur to any level). The name resolution is case insensitive.

@param qname [String,Loader::TypedName] the qualified name of the type to resolve @return [PAnyType,nil] the resolved type, or `nil` in case no type could be found

@api public

    # File lib/puppet/pops/types/p_type_set_type.rb
193 def [](qname)
194   if qname.is_a?(Loader::TypedName)
195     return nil unless qname.type == :type && qname.name_authority == @name_authority
196     qname = qname.name
197   end
198 
199   type = @types[qname] || @types[@dc_to_cc_map[qname.downcase]]
200   if type.nil? && !@references.empty?
201     segments = qname.split(TypeFormatter::NAME_SEGMENT_SEPARATOR)
202     first = segments[0]
203     type_set_ref = @references[first] || @references[@dc_to_cc_map[first.downcase]]
204     if type_set_ref.nil?
205       nil
206     else
207       type_set = type_set_ref.type_set
208       case segments.size
209       when 1
210         type_set
211       when 2
212         type_set[segments[1]]
213       else
214         segments.shift
215         type_set[segments.join(TypeFormatter::NAME_SEGMENT_SEPARATOR)]
216       end
217     end
218   else
219     type
220   end
221 end
_pcore_init_from_hash(init_hash) click to toggle source

@api private

    # File lib/puppet/pops/types/p_type_set_type.rb
106 def _pcore_init_from_hash(init_hash)
107   TypeAsserter.assert_instance_of('TypeSet initializer', TYPE_TYPESET_I12N, init_hash)
108 
109   # Name given to the loader have higher precedence than a name declared in the type
110   @name ||= init_hash[KEY_NAME].freeze
111   @name_authority ||= init_hash[KEY_NAME_AUTHORITY].freeze
112 
113   @pcore_version = PSemVerType.convert(init_hash[Pcore::KEY_PCORE_VERSION]).freeze
114   unless Pcore::PARSABLE_PCORE_VERSIONS.include?(@pcore_version)
115     raise ArgumentError,
116       "The pcore version for TypeSet '#{@name}' is not understood by this runtime. Expected range #{Pcore::PARSABLE_PCORE_VERSIONS}, got #{@pcore_version}"
117   end
118 
119   @pcore_uri = init_hash[Pcore::KEY_PCORE_URI].freeze
120   @version = PSemVerType.convert(init_hash[KEY_VERSION])
121   @types = init_hash[KEY_TYPES] || EMPTY_HASH
122   @types.freeze
123 
124   # Map downcase names to their camel-cased equivalent
125   @dc_to_cc_map = {}
126   @types.keys.each { |key| @dc_to_cc_map[key.downcase] = key }
127 
128   refs = init_hash[KEY_REFERENCES]
129   if refs.nil?
130     @references = EMPTY_HASH
131   else
132     ref_map = {}
133     root_map = Hash.new { |h, k| h[k] = {} }
134     refs.each do |ref_alias, ref|
135       ref = TypeSetReference.new(self, ref)
136 
137       # Protect against importing the exact same name_authority/name combination twice if the version ranges intersect
138       ref_name = ref.name
139       ref_na = ref.name_authority || @name_authority
140       na_roots = root_map[ref_na]
141 
142       ranges = na_roots[ref_name]
143       if ranges.nil?
144         na_roots[ref_name] = [ref.version_range]
145       else
146         unless ranges.all? { |range| (range & ref.version_range).nil? }
147           raise ArgumentError, "TypeSet '#{@name}' references TypeSet '#{ref_na}/#{ref_name}' more than once using overlapping version ranges"
148         end
149         ranges << ref.version_range
150       end
151 
152       if ref_map.has_key?(ref_alias)
153         raise ArgumentError, "TypeSet '#{@name}' references a TypeSet using alias '#{ref_alias}' more than once"
154       end
155       if @types.has_key?(ref_alias)
156         raise ArgumentError, "TypeSet '#{@name}' references a TypeSet using alias '#{ref_alias}'. The alias collides with the name of a declared type"
157       end
158       ref_map[ref_alias] = ref
159 
160       @dc_to_cc_map[ref_alias.downcase] = ref_alias
161       ref_map[ref_alias] = ref
162     end
163     @references = ref_map.freeze
164   end
165   @dc_to_cc_map.freeze
166   init_annotatable(init_hash)
167 end
_pcore_init_hash() click to toggle source

Produce a hash suitable for the initializer @return [Hash{String => Object}] the initialization hash

@api private

Calls superclass method Puppet::Pops::Types::Annotatable#_pcore_init_hash
    # File lib/puppet/pops/types/p_type_set_type.rb
173 def _pcore_init_hash
174   result = super()
175   result[Pcore::KEY_PCORE_URI] = @pcore_uri unless @pcore_uri.nil?
176   result[Pcore::KEY_PCORE_VERSION] =  @pcore_version.to_s
177   result[KEY_NAME_AUTHORITY] = @name_authority unless @name_authority.nil?
178   result[KEY_NAME] = @name
179   result[KEY_VERSION] = @version.to_s unless @version.nil?
180   result[KEY_TYPES] = @types unless @types.empty?
181   result[KEY_REFERENCES] = Hash[@references.map { |ref_alias, ref| [ref_alias, ref._pcore_init_hash] }] unless @references.empty?
182   result
183 end
accept(visitor, guard) click to toggle source
Calls superclass method Puppet::Pops::Types::PMetaType#accept
    # File lib/puppet/pops/types/p_type_set_type.rb
249 def accept(visitor, guard)
250   super
251   @types.each_value { |type| type.accept(visitor, guard) }
252   @references.each_value { |ref| ref.accept(visitor, guard) }
253 end
defines_type?(t) click to toggle source
    # File lib/puppet/pops/types/p_type_set_type.rb
223 def defines_type?(t)
224   !@types.key(t).nil?
225 end
eql?(o) click to toggle source
    # File lib/puppet/pops/types/p_type_set_type.rb
349 def eql?(o)
350   self.class == o.class && @name_authority == o.name_authority && @name == o.name && @version == o.version
351 end
hash() click to toggle source
    # File lib/puppet/pops/types/p_type_set_type.rb
345 def hash
346   @name_authority.hash ^ @name.hash ^ @version.hash
347 end
instance?(o, guard = nil) click to toggle source
    # File lib/puppet/pops/types/p_type_set_type.rb
353 def instance?(o, guard = nil)
354   o.is_a?(PTypeSetType)
355 end
label() click to toggle source

@api private

    # File lib/puppet/pops/types/p_type_set_type.rb
256 def label
257   "TypeSet '#{@name}'"
258 end
name_for(t, default_name) click to toggle source

Returns the name by which the given type is referenced from within this type set @param t [PAnyType] @return [String] the name by which the type is referenced within this type set

@api private

    # File lib/puppet/pops/types/p_type_set_type.rb
232 def name_for(t, default_name)
233   key = @types.key(t)
234   if key.nil?
235     if @references.empty?
236       default_name
237     else
238       @references.each_pair do |ref_key, ref|
239         ref_name = ref.type_set.name_for(t, nil)
240         return "#{ref_key}::#{ref_name}" unless ref_name.nil?
241       end
242       default_name
243     end
244   else
245     key
246   end
247 end
resolve(loader) click to toggle source

@api private

Calls superclass method Puppet::Pops::Types::PMetaType#resolve
    # File lib/puppet/pops/types/p_type_set_type.rb
261 def resolve(loader)
262   super
263   @references.each_value { |ref| ref.resolve(loader) }
264   tsa_loader = TypeSetLoader.new(self, loader)
265   @types.values.each { |type| type.resolve(tsa_loader) }
266   self
267 end
resolve_hash(loader, init_hash) click to toggle source

@api private

    # File lib/puppet/pops/types/p_type_set_type.rb
324 def resolve_hash(loader, init_hash)
325   result = Hash[init_hash.map do |key, value|
326     key = resolve_type_refs(loader, key)
327     value = resolve_type_refs(loader, value) unless key == KEY_TYPES && value.is_a?(Hash)
328     [key, value]
329   end]
330   name_auth = resolve_name_authority(result, loader)
331   types = result[KEY_TYPES]
332   if types.is_a?(Hash)
333     types.each do |type_name, value|
334       full_name = "#{@name}::#{type_name}"
335       typed_name = Loader::TypedName.new(:type, full_name, name_auth)
336       meta_name = value.is_a?(Hash) ? 'Object' : 'TypeAlias'
337       type = Loader::TypeDefinitionInstantiator.create_named_type(full_name, meta_name, value, name_auth)
338       loader.set_entry(typed_name, type)
339       types[type_name] = type
340     end
341   end
342   result
343 end
resolve_literal_hash(loader, init_hash_expression) click to toggle source

@api private

    # File lib/puppet/pops/types/p_type_set_type.rb
270 def resolve_literal_hash(loader, init_hash_expression)
271   result = {}
272   type_parser = TypeParser.singleton
273   init_hash_expression.entries.each do |entry|
274     key = type_parser.interpret_any(entry.key, loader)
275     if (key == KEY_TYPES || key == KEY_REFERENCES) && entry.value.is_a?(Model::LiteralHash)
276       # Skip type parser interpretation and convert qualified references directly to String keys.
277       hash = {}
278       entry.value.entries.each do |he|
279         kex = he.key
280         name = kex.is_a?(Model::QualifiedReference) ? kex.cased_value : type_parser.interpret_any(kex, loader)
281         hash[name] = key == KEY_TYPES ? he.value : type_parser.interpret_any(he.value, loader)
282       end
283       result[key] = hash
284     else
285       result[key] = type_parser.interpret_any(entry.value, loader)
286     end
287   end
288 
289   name_auth = resolve_name_authority(result, loader)
290 
291   types = result[KEY_TYPES]
292   if types.is_a?(Hash)
293     types.each do |type_name, value|
294       full_name = "#{@name}::#{type_name}"
295       typed_name = Loader::TypedName.new(:type, full_name, name_auth)
296       if value.is_a?(Model::ResourceDefaultsExpression)
297         # This is actually a <Parent> { <key-value entries> } notation. Convert to a literal hash that contains the parent
298         n = value.type_ref
299         name = n.cased_value
300         entries = []
301         unless name == 'Object' or name == 'TypeSet'
302           if value.operations.any? { |op| op.attribute_name == KEY_PARENT }
303             case Puppet[:strict]
304             when :warning
305               IssueReporter.warning(value, Issues::DUPLICATE_KEY, :key => KEY_PARENT)
306             when :error
307               IssueReporter.error(Puppet::ParseErrorWithIssue, value, Issues::DUPLICATE_KEY, :key => KEY_PARENT)
308             end
309           end
310           entries << Model::KeyedEntry.new(n.locator, n.offset, n.length, KEY_PARENT, n)
311         end
312         value.operations.each { |op| entries << Model::KeyedEntry.new(op.locator, op.offset, op.length, op.attribute_name, op.value_expr) }
313         value = Model::LiteralHash.new(value.locator, value.offset, value.length, entries)
314       end
315       type = Loader::TypeDefinitionInstantiator.create_type(full_name, value, name_auth)
316       loader.set_entry(typed_name, type, value.locator.to_uri(value))
317       types[type_name] = type
318     end
319   end
320   result
321 end

Protected Instance Methods

_assignable?(o, guard) click to toggle source

@api_private

    # File lib/puppet/pops/types/p_type_set_type.rb
368 def _assignable?(o, guard)
369   self.class == o.class && (self == DEFAULT || eql?(o))
370 end

Private Instance Methods

resolve_name_authority(init_hash, loader) click to toggle source
    # File lib/puppet/pops/types/p_type_set_type.rb
374 def resolve_name_authority(init_hash, loader)
375   name_auth = @name_authority
376   if name_auth.nil?
377     name_auth = init_hash[KEY_NAME_AUTHORITY]
378     name_auth = loader.name_authority if name_auth.nil? && loader.is_a?(TypeSetLoader)
379     if name_auth.nil?
380       name = @name || init_hash[KEY_NAME]
381       raise ArgumentError, "No 'name_authority' is declared in TypeSet '#{name}' and it cannot be inferred"
382     end
383   end
384   name_auth
385 end