| #include <sstream> | |
| #include "emitterutils.h" | |
| #include "indentation.h" // IWYU pragma: keep | |
| #include "yaml-cpp/emitter.h" | |
| #include "yaml-cpp/emitterdef.h" | |
| #include "yaml-cpp/emittermanip.h" | |
| #include "yaml-cpp/exceptions.h" // IWYU pragma: keep | |
| namespace YAML { | |
| class Binary; | |
| struct _Null; | |
| Emitter::Emitter() : m_pState(new EmitterState), m_stream{} {} | |
| Emitter::Emitter(std::ostream& stream) | |
| : m_pState(new EmitterState), m_stream(stream) {} | |
| Emitter::~Emitter() = default; | |
| const char* Emitter::c_str() const { return m_stream.str(); } | |
| std::size_t Emitter::size() const { return m_stream.pos(); } | |
| // state checking | |
| bool Emitter::good() const { return m_pState->good(); } | |
| const std::string Emitter::GetLastError() const { | |
| return m_pState->GetLastError(); | |
| } | |
| // global setters | |
| bool Emitter::SetOutputCharset(EMITTER_MANIP value) { | |
| return m_pState->SetOutputCharset(value, FmtScope::Global); | |
| } | |
| bool Emitter::SetStringFormat(EMITTER_MANIP value) { | |
| return m_pState->SetStringFormat(value, FmtScope::Global); | |
| } | |
| bool Emitter::SetBoolFormat(EMITTER_MANIP value) { | |
| bool ok = false; | |
| if (m_pState->SetBoolFormat(value, FmtScope::Global)) | |
| ok = true; | |
| if (m_pState->SetBoolCaseFormat(value, FmtScope::Global)) | |
| ok = true; | |
| if (m_pState->SetBoolLengthFormat(value, FmtScope::Global)) | |
| ok = true; | |
| return ok; | |
| } | |
| bool Emitter::SetNullFormat(EMITTER_MANIP value) { | |
| return m_pState->SetNullFormat(value, FmtScope::Global); | |
| } | |
| bool Emitter::SetIntBase(EMITTER_MANIP value) { | |
| return m_pState->SetIntFormat(value, FmtScope::Global); | |
| } | |
| bool Emitter::SetSeqFormat(EMITTER_MANIP value) { | |
| return m_pState->SetFlowType(GroupType::Seq, value, FmtScope::Global); | |
| } | |
| bool Emitter::SetMapFormat(EMITTER_MANIP value) { | |
| bool ok = false; | |
| if (m_pState->SetFlowType(GroupType::Map, value, FmtScope::Global)) | |
| ok = true; | |
| if (m_pState->SetMapKeyFormat(value, FmtScope::Global)) | |
| ok = true; | |
| return ok; | |
| } | |
| bool Emitter::SetIndent(std::size_t n) { | |
| return m_pState->SetIndent(n, FmtScope::Global); | |
| } | |
| bool Emitter::SetPreCommentIndent(std::size_t n) { | |
| return m_pState->SetPreCommentIndent(n, FmtScope::Global); | |
| } | |
| bool Emitter::SetPostCommentIndent(std::size_t n) { | |
| return m_pState->SetPostCommentIndent(n, FmtScope::Global); | |
| } | |
| bool Emitter::SetFloatPrecision(std::size_t n) { | |
| return m_pState->SetFloatPrecision(n, FmtScope::Global); | |
| } | |
| bool Emitter::SetDoublePrecision(std::size_t n) { | |
| return m_pState->SetDoublePrecision(n, FmtScope::Global); | |
| } | |
| void Emitter::RestoreGlobalModifiedSettings() { | |
| m_pState->RestoreGlobalModifiedSettings(); | |
| } | |
| // SetLocalValue | |
| // . Either start/end a group, or set a modifier locally | |
| Emitter& Emitter::SetLocalValue(EMITTER_MANIP value) { | |
| if (!good()) | |
| return *this; | |
| switch (value) { | |
| case BeginDoc: | |
| EmitBeginDoc(); | |
| break; | |
| case EndDoc: | |
| EmitEndDoc(); | |
| break; | |
| case BeginSeq: | |
| EmitBeginSeq(); | |
| break; | |
| case EndSeq: | |
| EmitEndSeq(); | |
| break; | |
| case BeginMap: | |
| EmitBeginMap(); | |
| break; | |
| case EndMap: | |
| EmitEndMap(); | |
| break; | |
| case Key: | |
| case Value: | |
| // deprecated (these can be deduced by the parity of nodes in a map) | |
| break; | |
| case TagByKind: | |
| EmitKindTag(); | |
| break; | |
| case Newline: | |
| EmitNewline(); | |
| break; | |
| default: | |
| m_pState->SetLocalValue(value); | |
| break; | |
| } | |
| return *this; | |
| } | |
| Emitter& Emitter::SetLocalIndent(const _Indent& indent) { | |
| m_pState->SetIndent(indent.value, FmtScope::Local); | |
| return *this; | |
| } | |
| Emitter& Emitter::SetLocalPrecision(const _Precision& precision) { | |
| if (precision.floatPrecision >= 0) | |
| m_pState->SetFloatPrecision(precision.floatPrecision, FmtScope::Local); | |
| if (precision.doublePrecision >= 0) | |
| m_pState->SetDoublePrecision(precision.doublePrecision, FmtScope::Local); | |
| return *this; | |
| } | |
| // EmitBeginDoc | |
| void Emitter::EmitBeginDoc() { | |
| if (!good()) | |
| return; | |
| if (m_pState->CurGroupType() != GroupType::NoType) { | |
| m_pState->SetError("Unexpected begin document"); | |
| return; | |
| } | |
| if (m_pState->HasAnchor() || m_pState->HasTag()) { | |
| m_pState->SetError("Unexpected begin document"); | |
| return; | |
| } | |
| if (m_stream.col() > 0) | |
| m_stream << "\n"; | |
| m_stream << "---\n"; | |
| m_pState->StartedDoc(); | |
| } | |
| // EmitEndDoc | |
| void Emitter::EmitEndDoc() { | |
| if (!good()) | |
| return; | |
| if (m_pState->CurGroupType() != GroupType::NoType) { | |
| m_pState->SetError("Unexpected begin document"); | |
| return; | |
| } | |
| if (m_pState->HasAnchor() || m_pState->HasTag()) { | |
| m_pState->SetError("Unexpected begin document"); | |
| return; | |
| } | |
| if (m_stream.col() > 0) | |
| m_stream << "\n"; | |
| m_stream << "...\n"; | |
| } | |
| // EmitBeginSeq | |
| void Emitter::EmitBeginSeq() { | |
| if (!good()) | |
| return; | |
| PrepareNode(m_pState->NextGroupType(GroupType::Seq)); | |
| m_pState->StartedGroup(GroupType::Seq); | |
| } | |
| // EmitEndSeq | |
| void Emitter::EmitEndSeq() { | |
| if (!good()) | |
| return; | |
| FlowType::value originalType = m_pState->CurGroupFlowType(); | |
| if (m_pState->CurGroupChildCount() == 0) | |
| m_pState->ForceFlow(); | |
| if (m_pState->CurGroupFlowType() == FlowType::Flow) { | |
| if (m_stream.comment()) | |
| m_stream << "\n"; | |
| if (originalType == FlowType::Block || m_pState->HasBegunNode()) | |
| m_stream << IndentTo(m_pState->CurIndent()); | |
| if (originalType == FlowType::Block) { | |
| m_stream << "["; | |
| } else { | |
| if (m_pState->CurGroupChildCount() == 0 && !m_pState->HasBegunNode()) | |
| m_stream << "["; | |
| } | |
| m_stream << "]"; | |
| } | |
| m_pState->EndedGroup(GroupType::Seq); | |
| } | |
| // EmitBeginMap | |
| void Emitter::EmitBeginMap() { | |
| if (!good()) | |
| return; | |
| PrepareNode(m_pState->NextGroupType(GroupType::Map)); | |
| m_pState->StartedGroup(GroupType::Map); | |
| } | |
| // EmitEndMap | |
| void Emitter::EmitEndMap() { | |
| if (!good()) | |
| return; | |
| FlowType::value originalType = m_pState->CurGroupFlowType(); | |
| if (m_pState->CurGroupChildCount() == 0) | |
| m_pState->ForceFlow(); | |
| if (m_pState->CurGroupFlowType() == FlowType::Flow) { | |
| if (m_stream.comment()) | |
| m_stream << "\n"; | |
| m_stream << IndentTo(m_pState->CurIndent()); | |
| if (originalType == FlowType::Block) { | |
| m_stream << "{"; | |
| } else { | |
| if (m_pState->CurGroupChildCount() == 0 && !m_pState->HasBegunNode()) | |
| m_stream << "{"; | |
| } | |
| m_stream << "}"; | |
| } | |
| m_pState->EndedGroup(GroupType::Map); | |
| } | |
| // EmitNewline | |
| void Emitter::EmitNewline() { | |
| if (!good()) | |
| return; | |
| PrepareNode(EmitterNodeType::NoType); | |
| m_stream << "\n"; | |
| m_pState->SetNonContent(); | |
| } | |
| bool Emitter::CanEmitNewline() const { return true; } | |
| // Put the stream in a state so we can simply write the next node | |
| // E.g., if we're in a sequence, write the "- " | |
| void Emitter::PrepareNode(EmitterNodeType::value child) { | |
| switch (m_pState->CurGroupNodeType()) { | |
| case EmitterNodeType::NoType: | |
| PrepareTopNode(child); | |
| break; | |
| case EmitterNodeType::FlowSeq: | |
| FlowSeqPrepareNode(child); | |
| break; | |
| case EmitterNodeType::BlockSeq: | |
| BlockSeqPrepareNode(child); | |
| break; | |
| case EmitterNodeType::FlowMap: | |
| FlowMapPrepareNode(child); | |
| break; | |
| case EmitterNodeType::BlockMap: | |
| BlockMapPrepareNode(child); | |
| break; | |
| case EmitterNodeType::Property: | |
| case EmitterNodeType::Scalar: | |
| assert(false); | |
| break; | |
| } | |
| } | |
| void Emitter::PrepareTopNode(EmitterNodeType::value child) { | |
| if (child == EmitterNodeType::NoType) | |
| return; | |
| if (m_pState->CurGroupChildCount() > 0 && m_stream.col() > 0) | |
| EmitBeginDoc(); | |
| switch (child) { | |
| case EmitterNodeType::NoType: | |
| break; | |
| case EmitterNodeType::Property: | |
| case EmitterNodeType::Scalar: | |
| case EmitterNodeType::FlowSeq: | |
| case EmitterNodeType::FlowMap: | |
| // TODO: if we were writing null, and | |
| // we wanted it blank, we wouldn't want a space | |
| SpaceOrIndentTo(m_pState->HasBegunContent(), 0); | |
| break; | |
| case EmitterNodeType::BlockSeq: | |
| case EmitterNodeType::BlockMap: | |
| if (m_pState->HasBegunNode()) | |
| m_stream << "\n"; | |
| break; | |
| } | |
| } | |
| void Emitter::FlowSeqPrepareNode(EmitterNodeType::value child) { | |
| const std::size_t lastIndent = m_pState->LastIndent(); | |
| if (!m_pState->HasBegunNode()) { | |
| if (m_stream.comment()) | |
| m_stream << "\n"; | |
| m_stream << IndentTo(lastIndent); | |
| if (m_pState->CurGroupChildCount() == 0) | |
| m_stream << "["; | |
| else | |
| m_stream << ","; | |
| } | |
| switch (child) { | |
| case EmitterNodeType::NoType: | |
| break; | |
| case EmitterNodeType::Property: | |
| case EmitterNodeType::Scalar: | |
| case EmitterNodeType::FlowSeq: | |
| case EmitterNodeType::FlowMap: | |
| SpaceOrIndentTo( | |
| m_pState->HasBegunContent() || m_pState->CurGroupChildCount() > 0, | |
| lastIndent); | |
| break; | |
| case EmitterNodeType::BlockSeq: | |
| case EmitterNodeType::BlockMap: | |
| assert(false); | |
| break; | |
| } | |
| } | |
| void Emitter::BlockSeqPrepareNode(EmitterNodeType::value child) { | |
| const std::size_t curIndent = m_pState->CurIndent(); | |
| const std::size_t nextIndent = curIndent + m_pState->CurGroupIndent(); | |
| if (child == EmitterNodeType::NoType) | |
| return; | |
| if (!m_pState->HasBegunContent()) { | |
| if (m_pState->CurGroupChildCount() > 0 || m_stream.comment()) { | |
| m_stream << "\n"; | |
| } | |
| m_stream << IndentTo(curIndent); | |
| m_stream << "-"; | |
| } | |
| switch (child) { | |
| case EmitterNodeType::NoType: | |
| break; | |
| case EmitterNodeType::Property: | |
| case EmitterNodeType::Scalar: | |
| case EmitterNodeType::FlowSeq: | |
| case EmitterNodeType::FlowMap: | |
| SpaceOrIndentTo(m_pState->HasBegunContent(), nextIndent); | |
| break; | |
| case EmitterNodeType::BlockSeq: | |
| m_stream << "\n"; | |
| break; | |
| case EmitterNodeType::BlockMap: | |
| if (m_pState->HasBegunContent() || m_stream.comment()) | |
| m_stream << "\n"; | |
| break; | |
| } | |
| } | |
| void Emitter::FlowMapPrepareNode(EmitterNodeType::value child) { | |
| if (m_pState->CurGroupChildCount() % 2 == 0) { | |
| if (m_pState->GetMapKeyFormat() == LongKey) | |
| m_pState->SetLongKey(); | |
| if (m_pState->CurGroupLongKey()) | |
| FlowMapPrepareLongKey(child); | |
| else | |
| FlowMapPrepareSimpleKey(child); | |
| } else { | |
| if (m_pState->CurGroupLongKey()) | |
| FlowMapPrepareLongKeyValue(child); | |
| else | |
| FlowMapPrepareSimpleKeyValue(child); | |
| } | |
| } | |
| void Emitter::FlowMapPrepareLongKey(EmitterNodeType::value child) { | |
| const std::size_t lastIndent = m_pState->LastIndent(); | |
| if (!m_pState->HasBegunNode()) { | |
| if (m_stream.comment()) | |
| m_stream << "\n"; | |
| m_stream << IndentTo(lastIndent); | |
| if (m_pState->CurGroupChildCount() == 0) | |
| m_stream << "{ ?"; | |
| else | |
| m_stream << ", ?"; | |
| } | |
| switch (child) { | |
| case EmitterNodeType::NoType: | |
| break; | |
| case EmitterNodeType::Property: | |
| case EmitterNodeType::Scalar: | |
| case EmitterNodeType::FlowSeq: | |
| case EmitterNodeType::FlowMap: | |
| SpaceOrIndentTo( | |
| m_pState->HasBegunContent() || m_pState->CurGroupChildCount() > 0, | |
| lastIndent); | |
| break; | |
| case EmitterNodeType::BlockSeq: | |
| case EmitterNodeType::BlockMap: | |
| assert(false); | |
| break; | |
| } | |
| } | |
| void Emitter::FlowMapPrepareLongKeyValue(EmitterNodeType::value child) { | |
| const std::size_t lastIndent = m_pState->LastIndent(); | |
| if (!m_pState->HasBegunNode()) { | |
| if (m_stream.comment()) | |
| m_stream << "\n"; | |
| m_stream << IndentTo(lastIndent); | |
| m_stream << ":"; | |
| } | |
| switch (child) { | |
| case EmitterNodeType::NoType: | |
| break; | |
| case EmitterNodeType::Property: | |
| case EmitterNodeType::Scalar: | |
| case EmitterNodeType::FlowSeq: | |
| case EmitterNodeType::FlowMap: | |
| SpaceOrIndentTo( | |
| m_pState->HasBegunContent() || m_pState->CurGroupChildCount() > 0, | |
| lastIndent); | |
| break; | |
| case EmitterNodeType::BlockSeq: | |
| case EmitterNodeType::BlockMap: | |
| assert(false); | |
| break; | |
| } | |
| } | |
| void Emitter::FlowMapPrepareSimpleKey(EmitterNodeType::value child) { | |
| const std::size_t lastIndent = m_pState->LastIndent(); | |
| if (!m_pState->HasBegunNode()) { | |
| if (m_stream.comment()) | |
| m_stream << "\n"; | |
| m_stream << IndentTo(lastIndent); | |
| if (m_pState->CurGroupChildCount() == 0) | |
| m_stream << "{"; | |
| else | |
| m_stream << ","; | |
| } | |
| switch (child) { | |
| case EmitterNodeType::NoType: | |
| break; | |
| case EmitterNodeType::Property: | |
| case EmitterNodeType::Scalar: | |
| case EmitterNodeType::FlowSeq: | |
| case EmitterNodeType::FlowMap: | |
| SpaceOrIndentTo( | |
| m_pState->HasBegunContent() || m_pState->CurGroupChildCount() > 0, | |
| lastIndent); | |
| break; | |
| case EmitterNodeType::BlockSeq: | |
| case EmitterNodeType::BlockMap: | |
| assert(false); | |
| break; | |
| } | |
| } | |
| void Emitter::FlowMapPrepareSimpleKeyValue(EmitterNodeType::value child) { | |
| const std::size_t lastIndent = m_pState->LastIndent(); | |
| if (!m_pState->HasBegunNode()) { | |
| if (m_stream.comment()) | |
| m_stream << "\n"; | |
| m_stream << IndentTo(lastIndent); | |
| if (m_pState->HasAlias()) { | |
| m_stream << " "; | |
| } | |
| m_stream << ":"; | |
| } | |
| switch (child) { | |
| case EmitterNodeType::NoType: | |
| break; | |
| case EmitterNodeType::Property: | |
| case EmitterNodeType::Scalar: | |
| case EmitterNodeType::FlowSeq: | |
| case EmitterNodeType::FlowMap: | |
| SpaceOrIndentTo( | |
| m_pState->HasBegunContent() || m_pState->CurGroupChildCount() > 0, | |
| lastIndent); | |
| break; | |
| case EmitterNodeType::BlockSeq: | |
| case EmitterNodeType::BlockMap: | |
| assert(false); | |
| break; | |
| } | |
| } | |
| void Emitter::BlockMapPrepareNode(EmitterNodeType::value child) { | |
| if (m_pState->CurGroupChildCount() % 2 == 0) { | |
| if (m_pState->GetMapKeyFormat() == LongKey) | |
| m_pState->SetLongKey(); | |
| if (child == EmitterNodeType::BlockSeq || | |
| child == EmitterNodeType::BlockMap || | |
| child == EmitterNodeType::Property) | |
| m_pState->SetLongKey(); | |
| if (m_pState->CurGroupLongKey()) | |
| BlockMapPrepareLongKey(child); | |
| else | |
| BlockMapPrepareSimpleKey(child); | |
| } else { | |
| if (m_pState->CurGroupLongKey()) | |
| BlockMapPrepareLongKeyValue(child); | |
| else | |
| BlockMapPrepareSimpleKeyValue(child); | |
| } | |
| } | |
| void Emitter::BlockMapPrepareLongKey(EmitterNodeType::value child) { | |
| const std::size_t curIndent = m_pState->CurIndent(); | |
| const std::size_t childCount = m_pState->CurGroupChildCount(); | |
| if (child == EmitterNodeType::NoType) | |
| return; | |
| if (!m_pState->HasBegunContent()) { | |
| if (childCount > 0) { | |
| m_stream << "\n"; | |
| } | |
| if (m_stream.comment()) { | |
| m_stream << "\n"; | |
| } | |
| m_stream << IndentTo(curIndent); | |
| m_stream << "?"; | |
| } | |
| switch (child) { | |
| case EmitterNodeType::NoType: | |
| break; | |
| case EmitterNodeType::Property: | |
| case EmitterNodeType::Scalar: | |
| case EmitterNodeType::FlowSeq: | |
| case EmitterNodeType::FlowMap: | |
| SpaceOrIndentTo(true, curIndent + 1); | |
| break; | |
| case EmitterNodeType::BlockSeq: | |
| case EmitterNodeType::BlockMap: | |
| if (m_pState->HasBegunContent()) | |
| m_stream << "\n"; | |
| break; | |
| } | |
| } | |
| void Emitter::BlockMapPrepareLongKeyValue(EmitterNodeType::value child) { | |
| const std::size_t curIndent = m_pState->CurIndent(); | |
| if (child == EmitterNodeType::NoType) | |
| return; | |
| if (!m_pState->HasBegunContent()) { | |
| m_stream << "\n"; | |
| m_stream << IndentTo(curIndent); | |
| m_stream << ":"; | |
| } | |
| switch (child) { | |
| case EmitterNodeType::NoType: | |
| break; | |
| case EmitterNodeType::Property: | |
| case EmitterNodeType::Scalar: | |
| case EmitterNodeType::FlowSeq: | |
| case EmitterNodeType::FlowMap: | |
| SpaceOrIndentTo(true, curIndent + 1); | |
| break; | |
| case EmitterNodeType::BlockSeq: | |
| case EmitterNodeType::BlockMap: | |
| if (m_pState->HasBegunContent()) | |
| m_stream << "\n"; | |
| SpaceOrIndentTo(true, curIndent + 1); | |
| break; | |
| } | |
| } | |
| void Emitter::BlockMapPrepareSimpleKey(EmitterNodeType::value child) { | |
| const std::size_t curIndent = m_pState->CurIndent(); | |
| const std::size_t childCount = m_pState->CurGroupChildCount(); | |
| if (child == EmitterNodeType::NoType) | |
| return; | |
| if (!m_pState->HasBegunNode()) { | |
| if (childCount > 0) { | |
| m_stream << "\n"; | |
| } | |
| } | |
| switch (child) { | |
| case EmitterNodeType::NoType: | |
| break; | |
| case EmitterNodeType::Property: | |
| case EmitterNodeType::Scalar: | |
| case EmitterNodeType::FlowSeq: | |
| case EmitterNodeType::FlowMap: | |
| SpaceOrIndentTo(m_pState->HasBegunContent(), curIndent); | |
| break; | |
| case EmitterNodeType::BlockSeq: | |
| case EmitterNodeType::BlockMap: | |
| break; | |
| } | |
| } | |
| void Emitter::BlockMapPrepareSimpleKeyValue(EmitterNodeType::value child) { | |
| const std::size_t curIndent = m_pState->CurIndent(); | |
| const std::size_t nextIndent = curIndent + m_pState->CurGroupIndent(); | |
| if (!m_pState->HasBegunNode()) { | |
| if (m_pState->HasAlias()) { | |
| m_stream << " "; | |
| } | |
| m_stream << ":"; | |
| } | |
| switch (child) { | |
| case EmitterNodeType::NoType: | |
| break; | |
| case EmitterNodeType::Property: | |
| case EmitterNodeType::Scalar: | |
| case EmitterNodeType::FlowSeq: | |
| case EmitterNodeType::FlowMap: | |
| SpaceOrIndentTo(true, nextIndent); | |
| break; | |
| case EmitterNodeType::BlockSeq: | |
| case EmitterNodeType::BlockMap: | |
| m_stream << "\n"; | |
| break; | |
| } | |
| } | |
| // SpaceOrIndentTo | |
| // . Prepares for some more content by proper spacing | |
| void Emitter::SpaceOrIndentTo(bool requireSpace, std::size_t indent) { | |
| if (m_stream.comment()) | |
| m_stream << "\n"; | |
| if (m_stream.col() > 0 && requireSpace) | |
| m_stream << " "; | |
| m_stream << IndentTo(indent); | |
| } | |
| void Emitter::PrepareIntegralStream(std::stringstream& stream) const { | |
| switch (m_pState->GetIntFormat()) { | |
| case Dec: | |
| stream << std::dec; | |
| break; | |
| case Hex: | |
| stream << "0x"; | |
| stream << std::hex; | |
| break; | |
| case Oct: | |
| stream << "0"; | |
| stream << std::oct; | |
| break; | |
| default: | |
| assert(false); | |
| } | |
| } | |
| void Emitter::StartedScalar() { m_pState->StartedScalar(); } | |
| // ******************************************************************************************* | |
| // overloads of Write | |
| StringEscaping::value GetStringEscapingStyle(const EMITTER_MANIP emitterManip) { | |
| switch (emitterManip) { | |
| case EscapeNonAscii: | |
| return StringEscaping::NonAscii; | |
| case EscapeAsJson: | |
| return StringEscaping::JSON; | |
| default: | |
| return StringEscaping::None; | |
| break; | |
| } | |
| } | |
| Emitter& Emitter::Write(const char* str, std::size_t size) { | |
| if (!good()) | |
| return *this; | |
| StringEscaping::value stringEscaping = GetStringEscapingStyle(m_pState->GetOutputCharset()); | |
| const StringFormat::value strFormat = | |
| Utils::ComputeStringFormat(str, size, m_pState->GetStringFormat(), | |
| m_pState->CurGroupFlowType(), stringEscaping == StringEscaping::NonAscii); | |
| if (strFormat == StringFormat::Literal || size > 1024) | |
| m_pState->SetMapKeyFormat(YAML::LongKey, FmtScope::Local); | |
| PrepareNode(EmitterNodeType::Scalar); | |
| switch (strFormat) { | |
| case StringFormat::Plain: | |
| m_stream.write(str, size); | |
| break; | |
| case StringFormat::SingleQuoted: | |
| Utils::WriteSingleQuotedString(m_stream, str, size); | |
| break; | |
| case StringFormat::DoubleQuoted: | |
| Utils::WriteDoubleQuotedString(m_stream, str, size, stringEscaping); | |
| break; | |
| case StringFormat::Literal: | |
| Utils::WriteLiteralString(m_stream, str, size, | |
| m_pState->CurIndent() + m_pState->GetIndent()); | |
| break; | |
| } | |
| StartedScalar(); | |
| return *this; | |
| } | |
| Emitter& Emitter::Write(const std::string& str) { | |
| return Write(str.data(), str.size()); | |
| } | |
| std::size_t Emitter::GetFloatPrecision() const { | |
| return m_pState->GetFloatPrecision(); | |
| } | |
| std::size_t Emitter::GetDoublePrecision() const { | |
| return m_pState->GetDoublePrecision(); | |
| } | |
| const char* Emitter::ComputeFullBoolName(bool b) const { | |
| const EMITTER_MANIP mainFmt = (m_pState->GetBoolLengthFormat() == ShortBool | |
| ? YesNoBool | |
| : m_pState->GetBoolFormat()); | |
| const EMITTER_MANIP caseFmt = m_pState->GetBoolCaseFormat(); | |
| switch (mainFmt) { | |
| case YesNoBool: | |
| switch (caseFmt) { | |
| case UpperCase: | |
| return b ? "YES" : "NO"; | |
| case CamelCase: | |
| return b ? "Yes" : "No"; | |
| case LowerCase: | |
| return b ? "yes" : "no"; | |
| default: | |
| break; | |
| } | |
| break; | |
| case OnOffBool: | |
| switch (caseFmt) { | |
| case UpperCase: | |
| return b ? "ON" : "OFF"; | |
| case CamelCase: | |
| return b ? "On" : "Off"; | |
| case LowerCase: | |
| return b ? "on" : "off"; | |
| default: | |
| break; | |
| } | |
| break; | |
| case TrueFalseBool: | |
| switch (caseFmt) { | |
| case UpperCase: | |
| return b ? "TRUE" : "FALSE"; | |
| case CamelCase: | |
| return b ? "True" : "False"; | |
| case LowerCase: | |
| return b ? "true" : "false"; | |
| default: | |
| break; | |
| } | |
| break; | |
| default: | |
| break; | |
| } | |
| return b ? "y" : "n"; // should never get here, but it can't hurt to give | |
| // these answers | |
| } | |
| const char* Emitter::ComputeNullName() const { | |
| switch (m_pState->GetNullFormat()) { | |
| case LowerNull: | |
| return "null"; | |
| case UpperNull: | |
| return "NULL"; | |
| case CamelNull: | |
| return "Null"; | |
| case TildeNull: | |
| // fallthrough | |
| default: | |
| return "~"; | |
| } | |
| } | |
| Emitter& Emitter::Write(bool b) { | |
| if (!good()) | |
| return *this; | |
| PrepareNode(EmitterNodeType::Scalar); | |
| const char* name = ComputeFullBoolName(b); | |
| if (m_pState->GetBoolLengthFormat() == ShortBool) | |
| m_stream << name[0]; | |
| else | |
| m_stream << name; | |
| StartedScalar(); | |
| return *this; | |
| } | |
| Emitter& Emitter::Write(char ch) { | |
| if (!good()) | |
| return *this; | |
| PrepareNode(EmitterNodeType::Scalar); | |
| Utils::WriteChar(m_stream, ch, GetStringEscapingStyle(m_pState->GetOutputCharset())); | |
| StartedScalar(); | |
| return *this; | |
| } | |
| Emitter& Emitter::Write(const _Alias& alias) { | |
| if (!good()) | |
| return *this; | |
| if (m_pState->HasAnchor() || m_pState->HasTag()) { | |
| m_pState->SetError(ErrorMsg::INVALID_ALIAS); | |
| return *this; | |
| } | |
| PrepareNode(EmitterNodeType::Scalar); | |
| if (!Utils::WriteAlias(m_stream, alias.content.data(), alias.content.size())) { | |
| m_pState->SetError(ErrorMsg::INVALID_ALIAS); | |
| return *this; | |
| } | |
| StartedScalar(); | |
| m_pState->SetAlias(); | |
| return *this; | |
| } | |
| Emitter& Emitter::Write(const _Anchor& anchor) { | |
| if (!good()) | |
| return *this; | |
| if (m_pState->HasAnchor()) { | |
| m_pState->SetError(ErrorMsg::INVALID_ANCHOR); | |
| return *this; | |
| } | |
| PrepareNode(EmitterNodeType::Property); | |
| if (!Utils::WriteAnchor(m_stream, anchor.content.data(), anchor.content.size())) { | |
| m_pState->SetError(ErrorMsg::INVALID_ANCHOR); | |
| return *this; | |
| } | |
| m_pState->SetAnchor(); | |
| return *this; | |
| } | |
| Emitter& Emitter::Write(const _Tag& tag) { | |
| if (!good()) | |
| return *this; | |
| if (m_pState->HasTag()) { | |
| m_pState->SetError(ErrorMsg::INVALID_TAG); | |
| return *this; | |
| } | |
| PrepareNode(EmitterNodeType::Property); | |
| bool success = false; | |
| if (tag.type == _Tag::Type::Verbatim) | |
| success = Utils::WriteTag(m_stream, tag.content, true); | |
| else if (tag.type == _Tag::Type::PrimaryHandle) | |
| success = Utils::WriteTag(m_stream, tag.content, false); | |
| else | |
| success = Utils::WriteTagWithPrefix(m_stream, tag.prefix, tag.content); | |
| if (!success) { | |
| m_pState->SetError(ErrorMsg::INVALID_TAG); | |
| return *this; | |
| } | |
| m_pState->SetTag(); | |
| return *this; | |
| } | |
| void Emitter::EmitKindTag() { Write(LocalTag("")); } | |
| Emitter& Emitter::Write(const _Comment& comment) { | |
| if (!good()) | |
| return *this; | |
| PrepareNode(EmitterNodeType::NoType); | |
| if (m_stream.col() > 0) | |
| m_stream << Indentation(m_pState->GetPreCommentIndent()); | |
| Utils::WriteComment(m_stream, comment.content.data(), comment.content.size(), | |
| m_pState->GetPostCommentIndent()); | |
| m_pState->SetNonContent(); | |
| return *this; | |
| } | |
| Emitter& Emitter::Write(const _Null& /*null*/) { | |
| if (!good()) | |
| return *this; | |
| PrepareNode(EmitterNodeType::Scalar); | |
| m_stream << ComputeNullName(); | |
| StartedScalar(); | |
| return *this; | |
| } | |
| Emitter& Emitter::Write(const Binary& binary) { | |
| Write(SecondaryTag("binary")); | |
| if (!good()) | |
| return *this; | |
| PrepareNode(EmitterNodeType::Scalar); | |
| Utils::WriteBinary(m_stream, binary); | |
| StartedScalar(); | |
| return *this; | |
| } | |
| } // namespace YAML |