/*
* libnbt++ - A library for the Minecraft Named Binary Tag format.
* Copyright (C) 2013, 2015 ljfa-ag
*
* This file is part of libnbt++.
*
* libnbt++ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libnbt++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libnbt++. If not, see .
*/
#ifndef TAG_LIST_H_INCLUDED
#define TAG_LIST_H_INCLUDED
#include "crtp_tag.h"
#include "tagfwd.h"
#include "value_initializer.h"
#include
#include
#include "nbt++_export.h"
namespace nbt
{
/**
* @brief Tag that contains multiple unnamed tags of the same type
*
* All the tags contained in the list have the same type, which can be queried
* with el_type(). The types of the values contained in the list should not
* be changed to mismatch the element type.
*
* If the list is empty, the type can be undetermined, in which case el_type()
* will return tag_type::Null. The type will then be set when the first tag
* is added to the list.
*/
class NBT___EXPORT tag_list final : public detail::crtp_tag
{
public:
//Iterator types
typedef std::vector::iterator iterator;
typedef std::vector::const_iterator const_iterator;
///The type of the tag
static constexpr tag_type type = tag_type::List;
/**
* @brief Constructs a list of type T with the given values
*
* Example: @code tag_list::of({3, 4, 5}) @endcode
* @param init list of values from which the elements are constructed
*/
template
static tag_list of(std::initializer_list init);
/**
* @brief Constructs an empty list
*
* The content type is determined when the first tag is added.
*/
tag_list(): tag_list(tag_type::Null) {}
///Constructs an empty list with the given content type
explicit tag_list(tag_type type): el_type_(type) {}
///Constructs a list with the given contents
tag_list(std::initializer_list init);
tag_list(std::initializer_list init);
tag_list(std::initializer_list init);
tag_list(std::initializer_list init);
tag_list(std::initializer_list init);
tag_list(std::initializer_list init);
tag_list(std::initializer_list init);
tag_list(std::initializer_list init);
tag_list(std::initializer_list init);
tag_list(std::initializer_list init);
tag_list(std::initializer_list init);
/**
* @brief Constructs a list with the given contents
* @throw std::invalid_argument if the tags are not all of the same type
*/
tag_list(std::initializer_list init);
/**
* @brief Accesses a tag by index with bounds checking
*
* Returns a value to the tag at the specified index, or throws an
* exception if it is out of range.
* @throw std::out_of_range if the index is out of range
*/
value& at(size_t i);
const value& at(size_t i) const;
/**
* @brief Accesses a tag by index
*
* Returns a value to the tag at the specified index. No bounds checking
* is performed.
*/
value& operator[](size_t i) { return tags[i]; }
const value& operator[](size_t i) const { return tags[i]; }
/**
* @brief Assigns a value at the given index
* @throw std::invalid_argument if the type of the value does not match the list's
* content type
* @throw std::out_of_range if the index is out of range
*/
void set(size_t i, value&& val);
/**
* @brief Appends the tag to the end of the list
* @throw std::invalid_argument if the type of the tag does not match the list's
* content type
*/
void push_back(value_initializer&& val);
/**
* @brief Constructs and appends a tag to the end of the list
* @throw std::invalid_argument if the type of the tag does not match the list's
* content type
*/
template
void emplace_back(Args&&... args);
///Removes the last element of the list
void pop_back() { tags.pop_back(); }
///Returns the content type of the list, or tag_type::Null if undetermined
tag_type el_type() const { return el_type_; }
///Returns the number of tags in the list
size_t size() const { return tags.size(); }
///Erases all tags from the list. Preserves the content type.
void clear() { tags.clear(); }
/**
* @brief Erases all tags from the list and changes the content type.
* @param type the new content type. Can be tag_type::Null to leave it undetermined.
*/
void reset(tag_type type = tag_type::Null);
//Iterators
iterator begin() { return tags.begin(); }
iterator end() { return tags.end(); }
const_iterator begin() const { return tags.begin(); }
const_iterator end() const { return tags.end(); }
const_iterator cbegin() const { return tags.cbegin(); }
const_iterator cend() const { return tags.cend(); }
/**
* @inheritdoc
* In case of a list of tag_end, the content type will be undetermined.
*/
void read_payload(io::stream_reader& reader) override;
/**
* @inheritdoc
* In case of a list of undetermined content type, the written type will be tag_end.
* @throw std::length_error if the list is too long for NBT
*/
void write_payload(io::stream_writer& writer) const override;
/**
* @brief Equality comparison for lists
*
* Lists are considered equal if their content types and the contained tags
* are equal.
*/
NBT___EXPORT friend bool operator==(const tag_list& lhs, const tag_list& rhs);
NBT___EXPORT friend bool operator!=(const tag_list& lhs, const tag_list& rhs);
private:
std::vector tags;
tag_type el_type_;
/**
* Internally used initialization function that initializes the list with
* tags of type T, with the constructor arguments of each T given by il.
* @param il list of values that are, one by one, given to a constructor of T
*/
template
void init(std::initializer_list il);
};
template
tag_list tag_list::of(std::initializer_list il)
{
tag_list result;
result.init(il);
return result;
}
template
void tag_list::emplace_back(Args&&... args)
{
if(el_type_ == tag_type::Null) //set content type if undetermined
el_type_ = T::type;
else if(el_type_ != T::type)
throw std::invalid_argument("The tag type does not match the list's content type");
tags.emplace_back(make_unique(std::forward(args)...));
}
template
void tag_list::init(std::initializer_list init)
{
el_type_ = T::type;
tags.reserve(init.size());
for(const Arg& arg: init)
tags.emplace_back(make_unique(arg));
}
}
#endif // TAG_LIST_H_INCLUDED