/* * 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 . */ #include "text/json_formatter.h" #include "nbt_tags.h" #include "nbt_visitor.h" #include #include #include namespace nbt { namespace text { namespace //anonymous { ///Helper class which uses the Visitor pattern to pretty-print tags class json_fmt_visitor : public const_nbt_visitor { public: json_fmt_visitor(std::ostream& os, const json_formatter& fmt): os(os) {} void visit(const tag_byte& b) override { os << static_cast(b.get()) << "b"; } //We don't want to print a character void visit(const tag_short& s) override { os << s.get() << "s"; } void visit(const tag_int& i) override { os << i.get(); } void visit(const tag_long& l) override { os << l.get() << "l"; } void visit(const tag_float& f) override { write_float(f.get()); os << "f"; } void visit(const tag_double& d) override { write_float(d.get()); os << "d"; } void visit(const tag_byte_array& ba) override { os << "[" << ba.size() << " bytes]"; } void visit(const tag_string& s) override { os << '"' << s.get() << '"'; } //TODO: escape special characters void visit(const tag_list& l) override { //Wrap lines for lists of lists or compounds. //Lists of other types can usually be on one line without problem. const bool break_lines = l.size() > 0 && (l.el_type() == tag_type::List || l.el_type() == tag_type::Compound); os << "["; if(break_lines) { os << "\n"; ++indent_lvl; for(unsigned int i = 0; i < l.size(); ++i) { indent(); if(l[i]) l[i].get().accept(*this); else write_null(); if(i != l.size()-1) os << ","; os << "\n"; } --indent_lvl; indent(); } else { for(unsigned int i = 0; i < l.size(); ++i) { if(l[i]) l[i].get().accept(*this); else write_null(); if(i != l.size()-1) os << ", "; } } os << "]"; } void visit(const tag_compound& c) override { if(c.size() == 0) //No line breaks inside empty compounds please { os << "{}"; return; } os << "{\n"; ++indent_lvl; unsigned int i = 0; for(const auto& kv: c) { indent(); os << kv.first << ": "; if(kv.second) kv.second.get().accept(*this); else write_null(); if(i != c.size()-1) os << ","; os << "\n"; ++i; } --indent_lvl; indent(); os << "}"; } void visit(const tag_int_array& ia) override { os << "["; for(unsigned int i = 0; i < ia.size(); ++i) { os << ia[i]; if(i != ia.size()-1) os << ", "; } os << "]"; } private: const std::string indent_str = " "; std::ostream& os; int indent_lvl = 0; void indent() { for(int i = 0; i < indent_lvl; ++i) os << indent_str; } template void write_float(T val, int precision = std::numeric_limits::max_digits10) { if(std::isfinite(val)) os << std::setprecision(precision) << val; else if(std::isinf(val)) { if(std::signbit(val)) os << "-"; os << "Infinity"; } else os << "NaN"; } void write_null() { os << "null"; } }; } void json_formatter::print(std::ostream& os, const tag& t) const { json_fmt_visitor v(os, *this); t.accept(v); } } }