# frozen_string_literal: true
require 'base64'
require 'time'
module Aws
module Xml
class Parser
class Frame
include Seahorse::Model::Shapes
class << self
def new(path, parent, ref, result = nil)
if self == Frame
frame = frame_class(ref).allocate
frame.send(:initialize, path, parent, ref, result)
frame
else
super
end
end
private
def frame_class(ref)
klass = FRAME_CLASSES[ref.shape.class]
if ListFrame == klass && (ref.shape.flattened || ref["flattened"])
FlatListFrame
elsif MapFrame == klass && (ref.shape.flattened || ref["flattened"])
MapEntryFrame
else
klass
end
end
end
def initialize(path, parent, ref, result)
@path = path
@parent = parent
@ref = ref
@result = result
@text = []
end
attr_reader :parent
attr_reader :ref
attr_reader :result
def set_text(value)
@text << value
end
def child_frame(xml_name)
NullFrame.new(xml_name, self)
end
def consume_child_frame(child); end
# @api private
def path
if Stack === parent
[@path]
else
parent.path + [@path]
end
end
# @api private
def yield_unhandled_value(path, value)
parent.yield_unhandled_value(path, value)
end
end
class StructureFrame < Frame
def initialize(xml_name, parent, ref, result = nil)
super
@result ||= ref.shape.struct_class.new
@members = {}
ref.shape.members.each do |member_name, member_ref|
apply_default_value(member_name, member_ref)
@members[xml_name(member_ref)] = {
name: member_name,
ref: member_ref,
}
end
end
def child_frame(xml_name)
if @member = @members[xml_name]
Frame.new(xml_name, self, @member[:ref])
elsif @ref.shape.union
UnknownMemberFrame.new(xml_name, self, nil, @result)
else
NullFrame.new(xml_name, self)
end
end
def consume_child_frame(child)
case child
when MapEntryFrame
@result[@member[:name]][child.key.result] = child.value.result
when FlatListFrame
@result[@member[:name]] << child.result
when UnknownMemberFrame
@result[:unknown] = { 'name' => child.path.last, 'value' => child.result }
when NullFrame
else
@result[@member[:name]] = child.result
end
if @ref.shape.union
# a union may only have one member set
# convert to the union subclass
# The default Struct created will have defaults set for all values
# This also sets only one of the values leaving everything else nil
# as required for unions
set_member_name = @member ? @member[:name] : :unknown
member_subclass = @ref.shape.member_subclass(set_member_name).new # shape.member_subclass(target.member).new
member_subclass[set_member_name] = @result[set_member_name]
@result = member_subclass
end
end
private
def apply_default_value(name, ref)
case ref.shape
when ListShape then @result[name] = DefaultList.new
when MapShape then @result[name] = DefaultMap.new
end
end
def xml_name(ref)
ref.location_name
end
def flattened_list?(ref)
ListShape === ref.shape && (ref.shape.flattened || ref["flattened"])
end
end
class ListFrame < Frame
def initialize(*args)
super
@result = []
@member_xml_name = @ref.shape.member.location_name || 'member'
end
def child_frame(xml_name)
if xml_name == @member_xml_name
Frame.new(xml_name, self, @ref.shape.member)
else
raise NotImplementedError
end
end
def consume_child_frame(child)
@result << child.result unless NullFrame === child
end
end
class FlatListFrame < Frame
def initialize(xml_name, *args)
super
@member = Frame.new(xml_name, self, @ref.shape.member)
end
def result
@member.result
end
def set_text(value)
@member.set_text(value)
end
def child_frame(xml_name)
@member.child_frame(xml_name)
end
def consume_child_frame(child)
@result = @member.result
end
end
class MapFrame < Frame
def initialize(*args)
super
@result = {}
end
def child_frame(xml_name)
if xml_name == 'entry'
MapEntryFrame.new(xml_name, self, @ref)
else
raise NotImplementedError
end
end
def consume_child_frame(child)
@result[child.key.result] = child.value.result
end
end
class MapEntryFrame < Frame
def initialize(xml_name, *args)
super
@key_name = @ref.shape.key.location_name || 'key'
@key = Frame.new(xml_name, self, @ref.shape.key)
@value_name = @ref.shape.value.location_name || 'value'
@value = Frame.new(xml_name, self, @ref.shape.value)
end
# @return [StringFrame]
attr_reader :key
# @return [Frame]
attr_reader :value
def child_frame(xml_name)
if @key_name == xml_name
@key
elsif @value_name == xml_name
@value
else
NullFrame.new(xml_name, self)
end
end
end
class NullFrame < Frame
def self.new(xml_name, parent)
super(xml_name, parent, nil, nil)
end
def set_text(value)
yield_unhandled_value(path, value)
super
end
end
class UnknownMemberFrame < Frame
def result
@text.join
end
end
class BlobFrame < Frame
def result
@text.empty? ? '' : Base64.decode64(@text.join)
end
end
class BooleanFrame < Frame
def result
@text.empty? ? nil : (@text.join == 'true')
end
end
class FloatFrame < Frame
def result
@text.empty? ? nil : Aws::Util.deserialize_number(@text.join)
end
end
class IntegerFrame < Frame
def result
@text.empty? ? nil : @text.join.to_i
end
end
class StringFrame < Frame
def result
@text.join
end
end
class TimestampFrame < Frame
def result
@text.empty? ? nil : Aws::Util.deserialize_time(@text.join)
end
end
include Seahorse::Model::Shapes
FRAME_CLASSES = {
NilClass => NullFrame,
BlobShape => BlobFrame,
BooleanShape => BooleanFrame,
FloatShape => FloatFrame,
IntegerShape => IntegerFrame,
ListShape => ListFrame,
MapShape => MapFrame,
StringShape => StringFrame,
StructureShape => StructureFrame,
UnionShape => StructureFrame,
TimestampShape => TimestampFrame,
}
end
end
end