// SPDX-FileCopyrightText: 2013, 2015 ljfa-ag // // SPDX-License-Identifier: LGPL-3.0-or-later /* * 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): 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 << '"'; write_escaped_string(s.get()); os << '"'; } 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 << "]"; } void visit(const tag_long_array& la) override { os << "["; for(unsigned int i = 0; i < la.size(); ++i) { os << la[i]; if(i != la.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 write_escaped_string(const std::string& str) { for (char c : str) { switch (c) { case '"': os << "\\\""; break; case '\\': os << "\\\\"; break; case '\b': os << "\\b"; break; case '\f': os << "\\f"; break; case '\n': os << "\\n"; break; case '\r': os << "\\r"; break; case '\t': os << "\\t"; break; default: if (c < 32 || c == 127) { // Control characters, escape as \u00XX os << "\\u00" << std::hex << std::setw(2) << std::setfill('0') << static_cast(c); } else { os << c; } break; } } } }; } void json_formatter::print(std::ostream& os, const tag& t) const { json_fmt_visitor v(os); t.accept(v); } } }