327 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			327 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#ifndef OSMIUM_IO_FILE_HPP
 | 
						|
#define OSMIUM_IO_FILE_HPP
 | 
						|
 | 
						|
/*
 | 
						|
 | 
						|
This file is part of Osmium (http://osmcode.org/libosmium).
 | 
						|
 | 
						|
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
 | 
						|
 | 
						|
Boost Software License - Version 1.0 - August 17th, 2003
 | 
						|
 | 
						|
Permission is hereby granted, free of charge, to any person or organization
 | 
						|
obtaining a copy of the software and accompanying documentation covered by
 | 
						|
this license (the "Software") to use, reproduce, display, distribute,
 | 
						|
execute, and transmit the Software, and to prepare derivative works of the
 | 
						|
Software, and to permit third-parties to whom the Software is furnished to
 | 
						|
do so, all subject to the following:
 | 
						|
 | 
						|
The copyright notices in the Software and this entire statement, including
 | 
						|
the above license grant, this restriction and the following disclaimer,
 | 
						|
must be included in all copies of the Software, in whole or in part, and
 | 
						|
all derivative works of the Software, unless such copies or derivative
 | 
						|
works are solely in the form of machine-executable object code generated by
 | 
						|
a source language processor.
 | 
						|
 | 
						|
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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 | 
						|
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 | 
						|
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 | 
						|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | 
						|
DEALINGS IN THE SOFTWARE.
 | 
						|
 | 
						|
*/
 | 
						|
 | 
						|
#include <cstddef>
 | 
						|
#include <sstream>
 | 
						|
#include <string>
 | 
						|
#include <vector>
 | 
						|
 | 
						|
#include <osmium/io/error.hpp>
 | 
						|
#include <osmium/io/file_format.hpp>
 | 
						|
#include <osmium/io/file_compression.hpp>
 | 
						|
#include <osmium/util/options.hpp>
 | 
						|
 | 
						|
namespace osmium {
 | 
						|
 | 
						|
    /**
 | 
						|
     * @brief Everything related to input and output of OSM data.
 | 
						|
     */
 | 
						|
    namespace io {
 | 
						|
 | 
						|
        namespace detail {
 | 
						|
 | 
						|
            inline std::vector<std::string> split(const std::string& in, const char delim) {
 | 
						|
                std::vector<std::string> result;
 | 
						|
                std::stringstream ss(in);
 | 
						|
                std::string item;
 | 
						|
                while (std::getline(ss, item, delim)) {
 | 
						|
                    result.push_back(item);
 | 
						|
                }
 | 
						|
                return result;
 | 
						|
            }
 | 
						|
 | 
						|
        } // namespace detail
 | 
						|
 | 
						|
        /**
 | 
						|
         * This class describes an OSM file in one of several different formats.
 | 
						|
         *
 | 
						|
         * If the filename is empty or "-", this means stdin or stdout is used.
 | 
						|
         */
 | 
						|
        class File : public osmium::util::Options {
 | 
						|
 | 
						|
        private:
 | 
						|
 | 
						|
            std::string m_filename;
 | 
						|
 | 
						|
            const char* m_buffer;
 | 
						|
            size_t m_buffer_size;
 | 
						|
 | 
						|
            std::string m_format_string;
 | 
						|
 | 
						|
            file_format m_file_format {file_format::unknown};
 | 
						|
 | 
						|
            file_compression m_file_compression {file_compression::none};
 | 
						|
 | 
						|
            bool m_has_multiple_object_versions {false};
 | 
						|
 | 
						|
        public:
 | 
						|
 | 
						|
            /**
 | 
						|
             * Create File using type and encoding from filename or given
 | 
						|
             * format specification.
 | 
						|
             *
 | 
						|
             * @param filename Filename including suffix. The type and encoding
 | 
						|
             *                 of the file will be taken from the suffix.
 | 
						|
             *                 An empty filename or "-" means stdin or stdout.
 | 
						|
             * @param format File format as string. See the description of the
 | 
						|
             *               parse_format() function for details. If this is
 | 
						|
             *               empty the format will be deduced from the suffix
 | 
						|
             *               of the filename.
 | 
						|
             */
 | 
						|
            explicit File(const std::string& filename = "", const std::string& format = "") :
 | 
						|
                Options(),
 | 
						|
                m_filename(filename),
 | 
						|
                m_buffer(nullptr),
 | 
						|
                m_buffer_size(0),
 | 
						|
                m_format_string(format) {
 | 
						|
 | 
						|
                // stdin/stdout
 | 
						|
                if (m_filename == "-") {
 | 
						|
                    m_filename = "";
 | 
						|
                }
 | 
						|
 | 
						|
                // if filename is a URL, default to XML format
 | 
						|
                const std::string protocol = m_filename.substr(0, m_filename.find_first_of(':'));
 | 
						|
                if (protocol == "http" || protocol == "https") {
 | 
						|
                    m_file_format = file_format::xml;
 | 
						|
                }
 | 
						|
 | 
						|
                if (format.empty()) {
 | 
						|
                    detect_format_from_suffix(m_filename);
 | 
						|
                } else {
 | 
						|
                    parse_format(format);
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            /**
 | 
						|
             * Create File using buffer pointer and size and type and encoding
 | 
						|
             * from given format specification.
 | 
						|
             *
 | 
						|
             * @param buffer Pointer to buffer with data.
 | 
						|
             * @param size   Size of buffer.
 | 
						|
             * @param format File format as string. See the description of the
 | 
						|
             *               parse_format() function for details.
 | 
						|
             */
 | 
						|
            explicit File(const char* buffer, size_t size, const std::string& format = "") :
 | 
						|
                Options(),
 | 
						|
                m_filename(),
 | 
						|
                m_buffer(buffer),
 | 
						|
                m_buffer_size(size),
 | 
						|
                m_format_string(format) {
 | 
						|
                if (format != "") {
 | 
						|
                    parse_format(format);
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            File(const File&) = default;
 | 
						|
            File& operator=(const File&) = default;
 | 
						|
 | 
						|
            File(File&&) = default;
 | 
						|
            File& operator=(File&&) = default;
 | 
						|
 | 
						|
            ~File() = default;
 | 
						|
 | 
						|
            const char* buffer() const noexcept {
 | 
						|
                return m_buffer;
 | 
						|
            }
 | 
						|
 | 
						|
            size_t buffer_size() const noexcept {
 | 
						|
                return m_buffer_size;
 | 
						|
            }
 | 
						|
 | 
						|
            void parse_format(const std::string& format) {
 | 
						|
                std::vector<std::string> options = detail::split(format, ',');
 | 
						|
 | 
						|
                // if the first item in the format list doesn't contain
 | 
						|
                // an equals sign, it is a format
 | 
						|
                if (!options.empty() && options[0].find_first_of('=') == std::string::npos) {
 | 
						|
                    detect_format_from_suffix(options[0]);
 | 
						|
                    options.erase(options.begin());
 | 
						|
                }
 | 
						|
 | 
						|
                for (auto& option : options) {
 | 
						|
                    const size_t pos = option.find_first_of('=');
 | 
						|
                    if (pos == std::string::npos) {
 | 
						|
                        set(option, true);
 | 
						|
                    } else {
 | 
						|
                        std::string value = option.substr(pos+1);
 | 
						|
                        option.erase(pos);
 | 
						|
                        set(option, value);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                if (get("history") == "true") {
 | 
						|
                    m_has_multiple_object_versions = true;
 | 
						|
                } else if (get("history") == "false") {
 | 
						|
                    m_has_multiple_object_versions = false;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            void detect_format_from_suffix(const std::string& name) {
 | 
						|
                std::vector<std::string> suffixes = detail::split(name, '.');
 | 
						|
 | 
						|
                if (suffixes.empty()) return;
 | 
						|
 | 
						|
                // if the last suffix is one of a known set of compressions,
 | 
						|
                // set that compression
 | 
						|
                if (suffixes.back() == "gz") {
 | 
						|
                    m_file_compression = file_compression::gzip;
 | 
						|
                    suffixes.pop_back();
 | 
						|
                } else if (suffixes.back() == "bz2") {
 | 
						|
                    m_file_compression = file_compression::bzip2;
 | 
						|
                    suffixes.pop_back();
 | 
						|
                }
 | 
						|
 | 
						|
                if (suffixes.empty()) return;
 | 
						|
 | 
						|
                // if the last suffix is one of a known set of formats,
 | 
						|
                // set that format
 | 
						|
                if (suffixes.back() == "pbf") {
 | 
						|
                    m_file_format = file_format::pbf;
 | 
						|
                    suffixes.pop_back();
 | 
						|
                } else if (suffixes.back() == "xml") {
 | 
						|
                    m_file_format = file_format::xml;
 | 
						|
                    suffixes.pop_back();
 | 
						|
                } else if (suffixes.back() == "opl") {
 | 
						|
                    m_file_format = file_format::opl;
 | 
						|
                    suffixes.pop_back();
 | 
						|
                } else if (suffixes.back() == "json") {
 | 
						|
                    m_file_format = file_format::json;
 | 
						|
                    suffixes.pop_back();
 | 
						|
                } else if (suffixes.back() == "o5m") {
 | 
						|
                    m_file_format = file_format::o5m;
 | 
						|
                    suffixes.pop_back();
 | 
						|
                } else if (suffixes.back() == "o5c") {
 | 
						|
                    m_file_format = file_format::o5m;
 | 
						|
                    m_has_multiple_object_versions = true;
 | 
						|
                    set("o5c_change_format", true);
 | 
						|
                    suffixes.pop_back();
 | 
						|
                } else if (suffixes.back() == "debug") {
 | 
						|
                    m_file_format = file_format::debug;
 | 
						|
                    suffixes.pop_back();
 | 
						|
                }
 | 
						|
 | 
						|
                if (suffixes.empty()) return;
 | 
						|
 | 
						|
                if (suffixes.back() == "osm") {
 | 
						|
                    if (m_file_format == file_format::unknown) m_file_format = file_format::xml;
 | 
						|
                    suffixes.pop_back();
 | 
						|
                } else if (suffixes.back() == "osh") {
 | 
						|
                    if (m_file_format == file_format::unknown) m_file_format = file_format::xml;
 | 
						|
                    m_has_multiple_object_versions = true;
 | 
						|
                    suffixes.pop_back();
 | 
						|
                } else if (suffixes.back() == "osc") {
 | 
						|
                    if (m_file_format == file_format::unknown) m_file_format = file_format::xml;
 | 
						|
                    m_has_multiple_object_versions = true;
 | 
						|
                    set("xml_change_format", true);
 | 
						|
                    suffixes.pop_back();
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            /**
 | 
						|
             * Check file format etc. for consistency and throw exception if
 | 
						|
             * there is a problem.
 | 
						|
             *
 | 
						|
             * @throws osmium::io_error
 | 
						|
             */
 | 
						|
            const File& check() const {
 | 
						|
                if (m_file_format == file_format::unknown) {
 | 
						|
                    std::string msg = "Could not detect file format";
 | 
						|
                    if (!m_format_string.empty())  {
 | 
						|
                        msg += " from format string '";
 | 
						|
                        msg += m_format_string;
 | 
						|
                        msg += "'";
 | 
						|
                    }
 | 
						|
                    if (m_filename.empty()) {
 | 
						|
                        msg += " for stdin/stdout";
 | 
						|
                    } else {
 | 
						|
                        msg += " for filename '";
 | 
						|
                        msg += m_filename;
 | 
						|
                        msg += "'";
 | 
						|
                    }
 | 
						|
                    msg += ".";
 | 
						|
                    throw io_error(msg);
 | 
						|
                }
 | 
						|
                return *this;
 | 
						|
            }
 | 
						|
 | 
						|
            file_format format() const noexcept {
 | 
						|
                return m_file_format;
 | 
						|
            }
 | 
						|
 | 
						|
            File& set_format(file_format format) noexcept {
 | 
						|
                m_file_format = format;
 | 
						|
                return *this;
 | 
						|
            }
 | 
						|
 | 
						|
            file_compression compression() const noexcept {
 | 
						|
                return m_file_compression;
 | 
						|
            }
 | 
						|
 | 
						|
            File& set_compression(file_compression compression) noexcept {
 | 
						|
                m_file_compression = compression;
 | 
						|
                return *this;
 | 
						|
            }
 | 
						|
 | 
						|
            bool has_multiple_object_versions() const noexcept {
 | 
						|
                return m_has_multiple_object_versions;
 | 
						|
            }
 | 
						|
 | 
						|
            File& set_has_multiple_object_versions(bool value) noexcept {
 | 
						|
                m_has_multiple_object_versions = value;
 | 
						|
                return *this;
 | 
						|
            }
 | 
						|
 | 
						|
            File& filename(const std::string& filename) {
 | 
						|
                if (filename == "-") {
 | 
						|
                    m_filename = "";
 | 
						|
                } else {
 | 
						|
                    m_filename = filename;
 | 
						|
                }
 | 
						|
                return *this;
 | 
						|
            }
 | 
						|
 | 
						|
            const std::string& filename() const noexcept {
 | 
						|
                return m_filename;
 | 
						|
            }
 | 
						|
 | 
						|
        }; // class File
 | 
						|
 | 
						|
    } // namespace io
 | 
						|
 | 
						|
} // namespace osmium
 | 
						|
 | 
						|
#endif // OSMIUM_IO_FILE_HPP
 |