lib/azure/storage/blob/serialization.rb in azure-storage-blob-1.1.0 vs lib/azure/storage/blob/serialization.rb in azure-storage-blob-2.0.0

- old
+ new

@@ -1,323 +1,348 @@ -# frozen_string_literal: true - -#------------------------------------------------------------------------- -# # Copyright (c) Microsoft and contributors. All rights reserved. -# -# The MIT License(MIT) - -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files(the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions : - -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -#-------------------------------------------------------------------------- -require "base64" - -module Azure::Storage - module Blob - module Serialization - include Azure::Storage::Common::Service::Serialization - - def self.container_enumeration_results_from_xml(xml) - xml = slopify(xml) - expect_node("EnumerationResults", xml) - - results = enumeration_results_from_xml(xml, Azure::Storage::Common::Service::EnumerationResults.new) - - return results unless (xml > "Containers").any? && ((xml > "Containers") > "Container").any? - - if xml.Containers.Container.count == 0 - results.push(container_from_xml(xml.Containers.Container)) - else - xml.Containers.Container.each { |container_node| - results.push(container_from_xml(container_node)) - } - end - - results - end - - def self.container_from_xml(xml) - xml = slopify(xml) - expect_node("Container", xml) - - Container::Container.new do |container| - container.name = xml.Name.text if (xml > "Name").any? - container.properties = container_properties_from_xml(xml.Properties) if (xml > "Properties").any? - container.metadata = metadata_from_xml(xml.Metadata) if (xml > "Metadata").any? - container.public_access_level = public_access_level_from_properties_xml(xml.Properties) if (xml > "Properties").any? - end - end - - def self.container_from_headers(headers) - Container::Container.new do |container| - container.properties = container_properties_from_headers(headers) - container.public_access_level = public_access_level_from_headers(headers) - container.metadata = metadata_from_headers(headers) - end - end - - def self.container_properties_from_xml(xml) - xml = slopify(xml) - expect_node("Properties", xml) - - props = {} - - props[:last_modified] = (xml > "Last-Modified").text if (xml > "Last-Modified").any? - props[:etag] = xml.Etag.text if (xml > "Etag").any? - props[:lease_status] = xml.LeaseStatus.text if (xml > "LeaseStatus").any? - props[:lease_state] = xml.LeaseState.text if (xml > "LeaseState").any? - props[:lease_duration] = xml.LeaseDuration.text if (xml > "LeaseDuration").any? - - props - end - - def self.container_properties_from_headers(headers) - props = {} - - props[:last_modified] = headers["Last-Modified"] - props[:etag] = headers["Etag"] - props[:lease_status] = headers["x-ms-lease-status"] - props[:lease_state] = headers["x-ms-lease-state"] - props[:lease_duration] = headers["x-ms-lease-duration"] - - props - end - - def self.public_access_level_from_headers(headers) - headers["x-ms-blob-public-access"] - end - - def self.public_access_level_from_properties_xml(xml) - (xml > "PublicAccess").any? ? xml.PublicAccess.text : nil - end - - def self.blob_enumeration_results_from_xml(xml) - xml = slopify(xml) - expect_node("EnumerationResults", xml) - - results = enumeration_results_from_xml(xml, Azure::Storage::Common::Service::EnumerationResults.new) - - return results unless (xml > "Blobs").any? - - if ((xml > "Blobs") > "Blob").any? - if xml.Blobs.Blob.count == 0 - results.push(blob_from_xml(xml.Blobs.Blob)) - else - xml.Blobs.Blob.each { |blob_node| - results.push(blob_from_xml(blob_node)) - } - end - end - - if ((xml > "Blobs") > "BlobPrefix").any? - if xml.Blobs.BlobPrefix.count == 0 - results.push(blob_prefix_from_xml(xml.Blobs.BlobPrefix)) - else - xml.Blobs.BlobPrefix.each { |blob_prefix| - results.push(blob_prefix_from_xml(blob_prefix)) - } - end - end - - results - end - - def self.blob_prefix_from_xml(xml) - xml = slopify(xml) - expect_node("BlobPrefix", xml) - - name = xml.Name.text if (xml > "Name").any? - name - end - - def self.blob_from_xml(xml) - xml = slopify(xml) - expect_node("Blob", xml) - - Blob.new do |blob| - blob.name = xml.Name.text if (xml > "Name").any? - blob.snapshot = xml.Snapshot.text if (xml > "Snapshot").any? - - blob.metadata = metadata_from_xml(xml.Metadata) if (xml > "Metadata").any? - if (xml > "Properties").any? - blob.properties = blob_properties_from_xml(xml.Properties) - blob.encrypted = xml.Properties.ServerEncrypted.text == "true" if (xml.Properties > "ServerEncrypted").any? - end - end - end - - def self.blob_from_headers(headers) - Blob.new do |blob| - blob.properties = blob_properties_from_headers(headers) - blob.metadata = metadata_from_headers(headers) - blob.encrypted = headers[Azure::Storage::Common::HeaderConstants::REQUEST_SERVER_ENCRYPTED] || headers[Azure::Storage::Common::HeaderConstants::SERVER_ENCRYPTED] - blob.encrypted = blob.encrypted.to_s == "true" unless blob.encrypted.nil? - end - end - - def self.blob_properties_from_xml(xml) - xml = slopify(xml) - expect_node("Properties", xml) - - props = {} - - props[:last_modified] = (xml > "Last-Modified").text if (xml > "Last-Modified").any? - props[:etag] = xml.Etag.text if (xml > "Etag").any? - props[:lease_status] = xml.LeaseStatus.text if (xml > "LeaseStatus").any? - props[:lease_state] = xml.LeaseState.text if (xml > "LeaseState").any? - props[:lease_duration] = xml.LeaseDuration.text if (xml > "LeaseDuration").any? - props[:content_length] = (xml > "Content-Length").text.to_i if (xml > "Content-Length").any? - props[:content_type] = (xml > "Content-Type").text if (xml > "Content-Type").any? - props[:content_encoding] = (xml > "Content-Encoding").text if (xml > "Content-Encoding").any? - props[:content_language] = (xml > "Content-Language").text if (xml > "Content-Language").any? - props[:content_md5] = (xml > "Content-MD5").text if (xml > "Content-MD5").any? - - props[:cache_control] = (xml > "Cache-Control").text if (xml > "Cache-Control").any? - props[:sequence_number] = (xml > "x-ms-blob-sequence-number").text.to_i if (xml > "x-ms-blob-sequence-number").any? - props[:blob_type] = xml.BlobType.text if (xml > "BlobType").any? - props[:copy_id] = xml.CopyId.text if (xml > "CopyId").any? - props[:copy_status] = xml.CopyStatus.text if (xml > "CopyStatus").any? - props[:copy_source] = xml.CopySource.text if (xml > "CopySource").any? - props[:copy_progress] = xml.CopyProgress.text if (xml > "CopyProgress").any? - props[:copy_completion_time] = xml.CopyCompletionTime.text if (xml > "CopyCompletionTime").any? - props[:copy_status_description] = xml.CopyStatusDescription.text if (xml > "CopyStatusDescription").any? - props[:incremental_copy] = xml.IncrementalCopy.text == "true" if (xml > "IncrementalCopy").any? - - props - end - - def self.blob_properties_from_headers(headers) - props = {} - - props[:last_modified] = headers["Last-Modified"] - props[:etag] = headers["Etag"] - props[:lease_status] = headers["x-ms-lease-status"] - props[:lease_state] = headers["x-ms-lease-state"] - props[:lease_duration] = headers["x-ms-lease-duration"] - - props[:content_length] = headers["x-ms-blob-content-length"] || headers["Content-Length"] - props[:content_length] = props[:content_length].to_i if props[:content_length] - - props[:content_type] = headers["x-ms-blob-content-type"] || headers["Content-Type"] - props[:content_encoding] = headers["x-ms-blob-content-encoding"] || headers["Content-Encoding"] - props[:content_language] = headers["x-ms-blob-content-language"] || headers["Content-Language"] - props[:content_disposition] = headers["x-ms-blob-content-disposition"] || headers["Content-Disposition"] - props[:content_md5] = headers["x-ms-blob-content-md5"] || headers["Content-MD5"] - props[:range_md5] = headers["Content-MD5"] if headers["x-ms-blob-content-md5"] && headers["Content-MD5"] - - props[:cache_control] = headers["x-ms-blob-cache-control"] || headers["Cache-Control"] - props[:sequence_number] = headers["x-ms-blob-sequence-number"].to_i if headers["x-ms-blob-sequence-number"] - props[:blob_type] = headers["x-ms-blob-type"] - - props[:copy_id] = headers["x-ms-copy-id"] - props[:copy_status] = headers["x-ms-copy-status"] - props[:copy_source] = headers["x-ms-copy-source"] - props[:copy_progress] = headers["x-ms-copy-progress"] - props[:copy_completion_time] = headers["x-ms-copy-completion-time"] - props[:copy_status_description] = headers["x-ms-copy-status-description"] - - props[:accept_ranges] = headers["Accept-Ranges"].to_i if headers["Accept-Ranges"] - - props[:append_offset] = headers["x-ms-blob-append-offset"].to_i if headers["x-ms-blob-append-offset"] - props[:committed_count] = headers["x-ms-blob-committed-block-count"].to_i if headers["x-ms-blob-committed-block-count"] - props[:incremental_copy] = headers["x-ms-incremental-copy"].to_s == "true" if headers["x-ms-incremental-copy"] - - props - end - - def self.block_list_to_xml(block_list) - builder = Nokogiri::XML::Builder.new(encoding: "UTF-8") do |xml| - xml.BlockList { - block_list.each { |block| - encoded_id = Base64.strict_encode64(block[0]) - case block[1] - when :uncommitted - xml.Uncommitted encoded_id - when :committed - xml.Committed encoded_id - else - xml.Latest encoded_id - end - } - } - end - builder.to_xml - end - - def self.block_list_from_xml(xml) - xml = slopify(xml) - expect_node("BlockList", xml) - - block_list = { - committed: [], - uncommitted: [] - } - - if ((xml > "CommittedBlocks") > "Block").any? - if xml.CommittedBlocks.Block.count == 0 - add_block(:committed, xml.CommittedBlocks.Block, block_list) - else - xml.CommittedBlocks.Block.each { |block_node| - add_block(:committed, block_node, block_list) - } - end - end - - return block_list unless (xml > "UncommittedBlocks") - - if ((xml > "UncommittedBlocks") > "Block").any? - if xml.UncommittedBlocks.Block.count == 0 - add_block(:uncommitted, xml.UncommittedBlocks.Block, block_list) - else - xml.UncommittedBlocks.Block.each { |block_node| - add_block(:uncommitted, block_node, block_list) - } - end - end - - block_list - end - - def self.add_block(type, block_node, block_list) - block = Block.new do |b| - b.name = Base64.strict_decode64(block_node.Name.text) if (block_node > "Name").any? - b.size = block_node.Size.text.to_i if (block_node > "Size").any? - b.type = type - end - block_list[type].push block - end - - def self.page_list_from_xml(xml) - xml = slopify(xml) - expect_node("PageList", xml) - - page_list = [] - - return page_list unless (xml > "PageRange").any? - - if xml.PageRange.count == 0 - page_list.push [xml.PageRange.Start.text.to_i, xml.PageRange.End.text.to_i] - else - xml.PageRange.each { |page_range| - page_list.push [page_range.Start.text.to_i, page_range.End.text.to_i] - } - end - - page_list - end - end - end -end +# frozen_string_literal: true + +#------------------------------------------------------------------------- +# # Copyright (c) Microsoft and contributors. All rights reserved. +# +# The MIT License(MIT) + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files(the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions : + +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +#-------------------------------------------------------------------------- +require "base64" + +module Azure::Storage + module Blob + module Serialization + include Azure::Storage::Common::Service::Serialization + + def self.container_enumeration_results_from_xml(xml) + xml = slopify(xml) + expect_node("EnumerationResults", xml) + + results = enumeration_results_from_xml(xml, Azure::Storage::Common::Service::EnumerationResults.new) + + return results unless (xml > "Containers").any? && ((xml > "Containers") > "Container").any? + + if xml.Containers.Container.count == 0 + results.push(container_from_xml(xml.Containers.Container)) + else + xml.Containers.Container.each { |container_node| + results.push(container_from_xml(container_node)) + } + end + + results + end + + def self.key_info_to_xml(start, expiry) + builder = Nokogiri::XML::Builder.new(encoding: "UTF-8") do |xml| + xml.KeyInfo { + xml.Start start.utc.iso8601 + xml.Expiry expiry.utc.iso8601 + } + end + builder.to_xml + end + + def self.user_delegation_key_from_xml(xml) + xml = slopify(xml) + expect_node("UserDelegationKey", xml) + + Azure::Storage::Common::Service::UserDelegationKey.new do |user_delegation_key| + user_delegation_key.signed_oid = xml.SignedOid.text if (xml > "SignedOid").any? + user_delegation_key.signed_tid = xml.SignedTid.text if (xml > "SignedTid").any? + user_delegation_key.signed_start = xml.SignedStart.text if (xml > "SignedStart").any? + user_delegation_key.signed_expiry = xml.SignedExpiry.text if (xml > "SignedExpiry").any? + user_delegation_key.signed_service = xml.SignedService.text if (xml > "SignedService").any? + user_delegation_key.signed_version = xml.SignedVersion.text if (xml > "SignedVersion").any? + user_delegation_key.value = xml.Value.text if (xml > "Value").any? + end + end + + def self.container_from_xml(xml) + xml = slopify(xml) + expect_node("Container", xml) + + Container::Container.new do |container| + container.name = xml.Name.text if (xml > "Name").any? + container.properties = container_properties_from_xml(xml.Properties) if (xml > "Properties").any? + container.metadata = metadata_from_xml(xml.Metadata) if (xml > "Metadata").any? + container.public_access_level = public_access_level_from_properties_xml(xml.Properties) if (xml > "Properties").any? + end + end + + def self.container_from_headers(headers) + Container::Container.new do |container| + container.properties = container_properties_from_headers(headers) + container.public_access_level = public_access_level_from_headers(headers) + container.metadata = metadata_from_headers(headers) + end + end + + def self.container_properties_from_xml(xml) + xml = slopify(xml) + expect_node("Properties", xml) + + props = {} + + props[:last_modified] = (xml > "Last-Modified").text if (xml > "Last-Modified").any? + props[:etag] = xml.Etag.text if (xml > "Etag").any? + props[:lease_status] = xml.LeaseStatus.text if (xml > "LeaseStatus").any? + props[:lease_state] = xml.LeaseState.text if (xml > "LeaseState").any? + props[:lease_duration] = xml.LeaseDuration.text if (xml > "LeaseDuration").any? + + props + end + + def self.container_properties_from_headers(headers) + props = {} + + props[:last_modified] = headers["Last-Modified"] + props[:etag] = headers["Etag"] + props[:lease_status] = headers["x-ms-lease-status"] + props[:lease_state] = headers["x-ms-lease-state"] + props[:lease_duration] = headers["x-ms-lease-duration"] + + props + end + + def self.public_access_level_from_headers(headers) + headers["x-ms-blob-public-access"] + end + + def self.public_access_level_from_properties_xml(xml) + (xml > "PublicAccess").any? ? xml.PublicAccess.text : nil + end + + def self.blob_enumeration_results_from_xml(xml) + xml = slopify(xml) + expect_node("EnumerationResults", xml) + + results = enumeration_results_from_xml(xml, Azure::Storage::Common::Service::EnumerationResults.new) + + return results unless (xml > "Blobs").any? + + if ((xml > "Blobs") > "Blob").any? + if xml.Blobs.Blob.count == 0 + results.push(blob_from_xml(xml.Blobs.Blob)) + else + xml.Blobs.Blob.each { |blob_node| + results.push(blob_from_xml(blob_node)) + } + end + end + + if ((xml > "Blobs") > "BlobPrefix").any? + if xml.Blobs.BlobPrefix.count == 0 + results.push(blob_prefix_from_xml(xml.Blobs.BlobPrefix)) + else + xml.Blobs.BlobPrefix.each { |blob_prefix| + results.push(blob_prefix_from_xml(blob_prefix)) + } + end + end + + results + end + + def self.blob_prefix_from_xml(xml) + xml = slopify(xml) + expect_node("BlobPrefix", xml) + + name = xml.Name.text if (xml > "Name").any? + name + end + + def self.blob_from_xml(xml) + xml = slopify(xml) + expect_node("Blob", xml) + + Blob.new do |blob| + blob.name = xml.Name.text if (xml > "Name").any? + blob.snapshot = xml.Snapshot.text if (xml > "Snapshot").any? + + blob.metadata = metadata_from_xml(xml.Metadata) if (xml > "Metadata").any? + if (xml > "Properties").any? + blob.properties = blob_properties_from_xml(xml.Properties) + blob.encrypted = xml.Properties.ServerEncrypted.text == "true" if (xml.Properties > "ServerEncrypted").any? + end + end + end + + def self.blob_from_headers(headers) + Blob.new do |blob| + blob.properties = blob_properties_from_headers(headers) + blob.metadata = metadata_from_headers(headers) + blob.encrypted = headers[Azure::Storage::Common::HeaderConstants::REQUEST_SERVER_ENCRYPTED] || headers[Azure::Storage::Common::HeaderConstants::SERVER_ENCRYPTED] + blob.encrypted = blob.encrypted.to_s == "true" unless blob.encrypted.nil? + end + end + + def self.blob_properties_from_xml(xml) + xml = slopify(xml) + expect_node("Properties", xml) + + props = {} + + props[:last_modified] = (xml > "Last-Modified").text if (xml > "Last-Modified").any? + props[:etag] = xml.Etag.text if (xml > "Etag").any? + props[:lease_status] = xml.LeaseStatus.text if (xml > "LeaseStatus").any? + props[:lease_state] = xml.LeaseState.text if (xml > "LeaseState").any? + props[:lease_duration] = xml.LeaseDuration.text if (xml > "LeaseDuration").any? + props[:content_length] = (xml > "Content-Length").text.to_i if (xml > "Content-Length").any? + props[:content_type] = (xml > "Content-Type").text if (xml > "Content-Type").any? + props[:content_encoding] = (xml > "Content-Encoding").text if (xml > "Content-Encoding").any? + props[:content_language] = (xml > "Content-Language").text if (xml > "Content-Language").any? + props[:content_md5] = (xml > "Content-MD5").text if (xml > "Content-MD5").any? + + props[:cache_control] = (xml > "Cache-Control").text if (xml > "Cache-Control").any? + props[:sequence_number] = (xml > "x-ms-blob-sequence-number").text.to_i if (xml > "x-ms-blob-sequence-number").any? + props[:blob_type] = xml.BlobType.text if (xml > "BlobType").any? + props[:copy_id] = xml.CopyId.text if (xml > "CopyId").any? + props[:copy_status] = xml.CopyStatus.text if (xml > "CopyStatus").any? + props[:copy_source] = xml.CopySource.text if (xml > "CopySource").any? + props[:copy_progress] = xml.CopyProgress.text if (xml > "CopyProgress").any? + props[:copy_completion_time] = xml.CopyCompletionTime.text if (xml > "CopyCompletionTime").any? + props[:copy_status_description] = xml.CopyStatusDescription.text if (xml > "CopyStatusDescription").any? + props[:incremental_copy] = xml.IncrementalCopy.text == "true" if (xml > "IncrementalCopy").any? + + props + end + + def self.blob_properties_from_headers(headers) + props = {} + + props[:last_modified] = headers["Last-Modified"] + props[:etag] = headers["Etag"] + props[:lease_status] = headers["x-ms-lease-status"] + props[:lease_state] = headers["x-ms-lease-state"] + props[:lease_duration] = headers["x-ms-lease-duration"] + + props[:content_length] = headers["x-ms-blob-content-length"] || headers["Content-Length"] + props[:content_length] = props[:content_length].to_i if props[:content_length] + + props[:content_type] = headers["x-ms-blob-content-type"] || headers["Content-Type"] + props[:content_encoding] = headers["x-ms-blob-content-encoding"] || headers["Content-Encoding"] + props[:content_language] = headers["x-ms-blob-content-language"] || headers["Content-Language"] + props[:content_disposition] = headers["x-ms-blob-content-disposition"] || headers["Content-Disposition"] + props[:content_md5] = headers["x-ms-blob-content-md5"] || headers["Content-MD5"] + props[:range_md5] = headers["Content-MD5"] if headers["x-ms-blob-content-md5"] && headers["Content-MD5"] + + props[:cache_control] = headers["x-ms-blob-cache-control"] || headers["Cache-Control"] + props[:sequence_number] = headers["x-ms-blob-sequence-number"].to_i if headers["x-ms-blob-sequence-number"] + props[:blob_type] = headers["x-ms-blob-type"] + + props[:copy_id] = headers["x-ms-copy-id"] + props[:copy_status] = headers["x-ms-copy-status"] + props[:copy_source] = headers["x-ms-copy-source"] + props[:copy_progress] = headers["x-ms-copy-progress"] + props[:copy_completion_time] = headers["x-ms-copy-completion-time"] + props[:copy_status_description] = headers["x-ms-copy-status-description"] + + props[:accept_ranges] = headers["Accept-Ranges"].to_i if headers["Accept-Ranges"] + + props[:append_offset] = headers["x-ms-blob-append-offset"].to_i if headers["x-ms-blob-append-offset"] + props[:committed_count] = headers["x-ms-blob-committed-block-count"].to_i if headers["x-ms-blob-committed-block-count"] + props[:incremental_copy] = headers["x-ms-incremental-copy"].to_s == "true" if headers["x-ms-incremental-copy"] + + props + end + + def self.block_list_to_xml(block_list) + builder = Nokogiri::XML::Builder.new(encoding: "UTF-8") do |xml| + xml.BlockList { + block_list.each { |block| + encoded_id = Base64.strict_encode64(block[0]) + case block[1] + when :uncommitted + xml.Uncommitted encoded_id + when :committed + xml.Committed encoded_id + else + xml.Latest encoded_id + end + } + } + end + builder.to_xml + end + + def self.block_list_from_xml(xml) + xml = slopify(xml) + expect_node("BlockList", xml) + + block_list = { + committed: [], + uncommitted: [] + } + + if ((xml > "CommittedBlocks") > "Block").any? + if xml.CommittedBlocks.Block.count == 0 + add_block(:committed, xml.CommittedBlocks.Block, block_list) + else + xml.CommittedBlocks.Block.each { |block_node| + add_block(:committed, block_node, block_list) + } + end + end + + return block_list unless (xml > "UncommittedBlocks") + + if ((xml > "UncommittedBlocks") > "Block").any? + if xml.UncommittedBlocks.Block.count == 0 + add_block(:uncommitted, xml.UncommittedBlocks.Block, block_list) + else + xml.UncommittedBlocks.Block.each { |block_node| + add_block(:uncommitted, block_node, block_list) + } + end + end + + block_list + end + + def self.add_block(type, block_node, block_list) + block = Block.new do |b| + b.name = Base64.strict_decode64(block_node.Name.text) if (block_node > "Name").any? + b.size = block_node.Size.text.to_i if (block_node > "Size").any? + b.type = type + end + block_list[type].push block + end + + def self.page_list_from_xml(xml) + xml = slopify(xml) + expect_node("PageList", xml) + + page_list = [] + + return page_list unless (xml > "PageRange").any? + + if xml.PageRange.count == 0 + page_list.push [xml.PageRange.Start.text.to_i, xml.PageRange.End.text.to_i] + else + xml.PageRange.each { |page_range| + page_list.push [page_range.Start.text.to_i, page_range.End.text.to_i] + } + end + + page_list + end + end + end +end