|  | #include <limits> | 
|  |  | 
|  | #include "emitterstate.h" | 
|  | #include "yaml-cpp/exceptions.h"  // IWYU pragma: keep | 
|  |  | 
|  | namespace YAML { | 
|  | EmitterState::EmitterState() | 
|  | : m_isGood(true), | 
|  | m_lastError{}, | 
|  | // default global manipulators | 
|  | m_charset(EmitNonAscii), | 
|  | m_strFmt(Auto), | 
|  | m_boolFmt(TrueFalseBool), | 
|  | m_boolLengthFmt(LongBool), | 
|  | m_boolCaseFmt(LowerCase), | 
|  | m_nullFmt(TildeNull), | 
|  | m_intFmt(Dec), | 
|  | m_indent(2), | 
|  | m_preCommentIndent(2), | 
|  | m_postCommentIndent(1), | 
|  | m_seqFmt(Block), | 
|  | m_mapFmt(Block), | 
|  | m_mapKeyFmt(Auto), | 
|  | m_floatPrecision(std::numeric_limits<float>::max_digits10), | 
|  | m_doublePrecision(std::numeric_limits<double>::max_digits10), | 
|  | // | 
|  | m_modifiedSettings{}, | 
|  | m_globalModifiedSettings{}, | 
|  | m_groups{}, | 
|  | m_curIndent(0), | 
|  | m_hasAnchor(false), | 
|  | m_hasAlias(false), | 
|  | m_hasTag(false), | 
|  | m_hasNonContent(false), | 
|  | m_docCount(0) {} | 
|  |  | 
|  | EmitterState::~EmitterState() = default; | 
|  |  | 
|  | // SetLocalValue | 
|  | // . We blindly tries to set all possible formatters to this value | 
|  | // . Only the ones that make sense will be accepted | 
|  | void EmitterState::SetLocalValue(EMITTER_MANIP value) { | 
|  | SetOutputCharset(value, FmtScope::Local); | 
|  | SetStringFormat(value, FmtScope::Local); | 
|  | SetBoolFormat(value, FmtScope::Local); | 
|  | SetBoolCaseFormat(value, FmtScope::Local); | 
|  | SetBoolLengthFormat(value, FmtScope::Local); | 
|  | SetNullFormat(value, FmtScope::Local); | 
|  | SetIntFormat(value, FmtScope::Local); | 
|  | SetFlowType(GroupType::Seq, value, FmtScope::Local); | 
|  | SetFlowType(GroupType::Map, value, FmtScope::Local); | 
|  | SetMapKeyFormat(value, FmtScope::Local); | 
|  | } | 
|  |  | 
|  | void EmitterState::SetAnchor() { m_hasAnchor = true; } | 
|  |  | 
|  | void EmitterState::SetAlias() { m_hasAlias = true; } | 
|  |  | 
|  | void EmitterState::SetTag() { m_hasTag = true; } | 
|  |  | 
|  | void EmitterState::SetNonContent() { m_hasNonContent = true; } | 
|  |  | 
|  | void EmitterState::SetLongKey() { | 
|  | assert(!m_groups.empty()); | 
|  | if (m_groups.empty()) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | assert(m_groups.back()->type == GroupType::Map); | 
|  | m_groups.back()->longKey = true; | 
|  | } | 
|  |  | 
|  | void EmitterState::ForceFlow() { | 
|  | assert(!m_groups.empty()); | 
|  | if (m_groups.empty()) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | m_groups.back()->flowType = FlowType::Flow; | 
|  | } | 
|  |  | 
|  | void EmitterState::StartedNode() { | 
|  | if (m_groups.empty()) { | 
|  | m_docCount++; | 
|  | } else { | 
|  | m_groups.back()->childCount++; | 
|  | if (m_groups.back()->childCount % 2 == 0) { | 
|  | m_groups.back()->longKey = false; | 
|  | } | 
|  | } | 
|  |  | 
|  | m_hasAnchor = false; | 
|  | m_hasAlias = false; | 
|  | m_hasTag = false; | 
|  | m_hasNonContent = false; | 
|  | } | 
|  |  | 
|  | EmitterNodeType::value EmitterState::NextGroupType( | 
|  | GroupType::value type) const { | 
|  | if (type == GroupType::Seq) { | 
|  | if (GetFlowType(type) == Block) | 
|  | return EmitterNodeType::BlockSeq; | 
|  | return EmitterNodeType::FlowSeq; | 
|  | } | 
|  |  | 
|  | if (GetFlowType(type) == Block) | 
|  | return EmitterNodeType::BlockMap; | 
|  | return EmitterNodeType::FlowMap; | 
|  |  | 
|  | // can't happen | 
|  | assert(false); | 
|  | return EmitterNodeType::NoType; | 
|  | } | 
|  |  | 
|  | void EmitterState::StartedDoc() { | 
|  | m_hasAnchor = false; | 
|  | m_hasTag = false; | 
|  | m_hasNonContent = false; | 
|  | } | 
|  |  | 
|  | void EmitterState::EndedDoc() { | 
|  | m_hasAnchor = false; | 
|  | m_hasTag = false; | 
|  | m_hasNonContent = false; | 
|  | } | 
|  |  | 
|  | void EmitterState::StartedScalar() { | 
|  | StartedNode(); | 
|  | ClearModifiedSettings(); | 
|  | } | 
|  |  | 
|  | void EmitterState::StartedGroup(GroupType::value type) { | 
|  | StartedNode(); | 
|  |  | 
|  | const std::size_t lastGroupIndent = | 
|  | (m_groups.empty() ? 0 : m_groups.back()->indent); | 
|  | m_curIndent += lastGroupIndent; | 
|  |  | 
|  | // TODO: Create move constructors for settings types to simplify transfer | 
|  | std::unique_ptr<Group> pGroup(new Group(type)); | 
|  |  | 
|  | // transfer settings (which last until this group is done) | 
|  | // | 
|  | // NB: if pGroup->modifiedSettings == m_modifiedSettings, | 
|  | // m_modifiedSettings is not changed! | 
|  | pGroup->modifiedSettings = std::move(m_modifiedSettings); | 
|  |  | 
|  | // set up group | 
|  | if (GetFlowType(type) == Block) { | 
|  | pGroup->flowType = FlowType::Block; | 
|  | } else { | 
|  | pGroup->flowType = FlowType::Flow; | 
|  | } | 
|  | pGroup->indent = GetIndent(); | 
|  |  | 
|  | m_groups.push_back(std::move(pGroup)); | 
|  | } | 
|  |  | 
|  | void EmitterState::EndedGroup(GroupType::value type) { | 
|  | if (m_groups.empty()) { | 
|  | if (type == GroupType::Seq) { | 
|  | return SetError(ErrorMsg::UNEXPECTED_END_SEQ); | 
|  | } | 
|  | return SetError(ErrorMsg::UNEXPECTED_END_MAP); | 
|  | } | 
|  |  | 
|  | if (m_hasTag) { | 
|  | SetError(ErrorMsg::INVALID_TAG); | 
|  | } | 
|  | if (m_hasAnchor) { | 
|  | SetError(ErrorMsg::INVALID_ANCHOR); | 
|  | } | 
|  |  | 
|  | // get rid of the current group | 
|  | { | 
|  | std::unique_ptr<Group> pFinishedGroup = std::move(m_groups.back()); | 
|  | m_groups.pop_back(); | 
|  | if (pFinishedGroup->type != type) { | 
|  | return SetError(ErrorMsg::UNMATCHED_GROUP_TAG); | 
|  | } | 
|  | } | 
|  |  | 
|  | // reset old settings | 
|  | std::size_t lastIndent = (m_groups.empty() ? 0 : m_groups.back()->indent); | 
|  | assert(m_curIndent >= lastIndent); | 
|  | m_curIndent -= lastIndent; | 
|  |  | 
|  | // some global settings that we changed may have been overridden | 
|  | // by a local setting we just popped, so we need to restore them | 
|  | m_globalModifiedSettings.restore(); | 
|  |  | 
|  | ClearModifiedSettings(); | 
|  | m_hasAnchor = false; | 
|  | m_hasTag = false; | 
|  | m_hasNonContent = false; | 
|  | } | 
|  |  | 
|  | EmitterNodeType::value EmitterState::CurGroupNodeType() const { | 
|  | if (m_groups.empty()) { | 
|  | return EmitterNodeType::NoType; | 
|  | } | 
|  |  | 
|  | return m_groups.back()->NodeType(); | 
|  | } | 
|  |  | 
|  | GroupType::value EmitterState::CurGroupType() const { | 
|  | return m_groups.empty() ? GroupType::NoType : m_groups.back()->type; | 
|  | } | 
|  |  | 
|  | FlowType::value EmitterState::CurGroupFlowType() const { | 
|  | return m_groups.empty() ? FlowType::NoType : m_groups.back()->flowType; | 
|  | } | 
|  |  | 
|  | std::size_t EmitterState::CurGroupIndent() const { | 
|  | return m_groups.empty() ? 0 : m_groups.back()->indent; | 
|  | } | 
|  |  | 
|  | std::size_t EmitterState::CurGroupChildCount() const { | 
|  | return m_groups.empty() ? m_docCount : m_groups.back()->childCount; | 
|  | } | 
|  |  | 
|  | bool EmitterState::CurGroupLongKey() const { | 
|  | return m_groups.empty() ? false : m_groups.back()->longKey; | 
|  | } | 
|  |  | 
|  | std::size_t EmitterState::LastIndent() const { | 
|  | if (m_groups.size() <= 1) { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | return m_curIndent - m_groups[m_groups.size() - 2]->indent; | 
|  | } | 
|  |  | 
|  | void EmitterState::ClearModifiedSettings() { m_modifiedSettings.clear(); } | 
|  |  | 
|  | void EmitterState::RestoreGlobalModifiedSettings() { | 
|  | m_globalModifiedSettings.restore(); | 
|  | } | 
|  |  | 
|  | bool EmitterState::SetOutputCharset(EMITTER_MANIP value, | 
|  | FmtScope::value scope) { | 
|  | switch (value) { | 
|  | case EmitNonAscii: | 
|  | case EscapeNonAscii: | 
|  | case EscapeAsJson: | 
|  | _Set(m_charset, value, scope); | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool EmitterState::SetStringFormat(EMITTER_MANIP value, FmtScope::value scope) { | 
|  | switch (value) { | 
|  | case Auto: | 
|  | case SingleQuoted: | 
|  | case DoubleQuoted: | 
|  | case Literal: | 
|  | _Set(m_strFmt, value, scope); | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool EmitterState::SetBoolFormat(EMITTER_MANIP value, FmtScope::value scope) { | 
|  | switch (value) { | 
|  | case OnOffBool: | 
|  | case TrueFalseBool: | 
|  | case YesNoBool: | 
|  | _Set(m_boolFmt, value, scope); | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool EmitterState::SetBoolLengthFormat(EMITTER_MANIP value, | 
|  | FmtScope::value scope) { | 
|  | switch (value) { | 
|  | case LongBool: | 
|  | case ShortBool: | 
|  | _Set(m_boolLengthFmt, value, scope); | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool EmitterState::SetBoolCaseFormat(EMITTER_MANIP value, | 
|  | FmtScope::value scope) { | 
|  | switch (value) { | 
|  | case UpperCase: | 
|  | case LowerCase: | 
|  | case CamelCase: | 
|  | _Set(m_boolCaseFmt, value, scope); | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool EmitterState::SetNullFormat(EMITTER_MANIP value, FmtScope::value scope) { | 
|  | switch (value) { | 
|  | case LowerNull: | 
|  | case UpperNull: | 
|  | case CamelNull: | 
|  | case TildeNull: | 
|  | _Set(m_nullFmt, value, scope); | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool EmitterState::SetIntFormat(EMITTER_MANIP value, FmtScope::value scope) { | 
|  | switch (value) { | 
|  | case Dec: | 
|  | case Hex: | 
|  | case Oct: | 
|  | _Set(m_intFmt, value, scope); | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool EmitterState::SetIndent(std::size_t value, FmtScope::value scope) { | 
|  | if (value <= 1) | 
|  | return false; | 
|  |  | 
|  | _Set(m_indent, value, scope); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool EmitterState::SetPreCommentIndent(std::size_t value, | 
|  | FmtScope::value scope) { | 
|  | if (value == 0) | 
|  | return false; | 
|  |  | 
|  | _Set(m_preCommentIndent, value, scope); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool EmitterState::SetPostCommentIndent(std::size_t value, | 
|  | FmtScope::value scope) { | 
|  | if (value == 0) | 
|  | return false; | 
|  |  | 
|  | _Set(m_postCommentIndent, value, scope); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool EmitterState::SetFlowType(GroupType::value groupType, EMITTER_MANIP value, | 
|  | FmtScope::value scope) { | 
|  | switch (value) { | 
|  | case Block: | 
|  | case Flow: | 
|  | _Set(groupType == GroupType::Seq ? m_seqFmt : m_mapFmt, value, scope); | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | EMITTER_MANIP EmitterState::GetFlowType(GroupType::value groupType) const { | 
|  | // force flow style if we're currently in a flow | 
|  | if (CurGroupFlowType() == FlowType::Flow) | 
|  | return Flow; | 
|  |  | 
|  | // otherwise, go with what's asked of us | 
|  | return (groupType == GroupType::Seq ? m_seqFmt.get() : m_mapFmt.get()); | 
|  | } | 
|  |  | 
|  | bool EmitterState::SetMapKeyFormat(EMITTER_MANIP value, FmtScope::value scope) { | 
|  | switch (value) { | 
|  | case Auto: | 
|  | case LongKey: | 
|  | _Set(m_mapKeyFmt, value, scope); | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool EmitterState::SetFloatPrecision(std::size_t value, FmtScope::value scope) { | 
|  | if (value > std::numeric_limits<float>::max_digits10) | 
|  | return false; | 
|  | _Set(m_floatPrecision, value, scope); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool EmitterState::SetDoublePrecision(std::size_t value, | 
|  | FmtScope::value scope) { | 
|  | if (value > std::numeric_limits<double>::max_digits10) | 
|  | return false; | 
|  | _Set(m_doublePrecision, value, scope); | 
|  | return true; | 
|  | } | 
|  | }  // namespace YAML |