//////////////////////////////////////////////////////////////////////////////// /// \file YamlTree.cpp /// \author excel96 /// \date 2006.4.21 //////////////////////////////////////////////////////////////////////////////// #include "PCH.h" #include "YamlTree.h" namespace { const std::string YAML_WHITESPACES = " \t\r\n"; const std::string YAML_LINEFEEDS = "\r\n"; const std::string YAML_INDENT = " \t"; const char YAML_COLON = ':'; const char YAML_SHARP = '#'; const char YAML_MINUS = '-'; const char YAML_AMPERSAND = '&'; const char YAML_ASTERISK = '*'; const std::string YAML_PIPE = "|"; const std::string YAML_RIGHT_BRACKET = ">"; //const std::string YAML_DOCUMENT_HEADER = "---"; //const std::string YAML_DOCUMENT_TERMINATOR = "..."; /// 한 라인에 대한 파싱 결과 enum ParseResult { RESULT_NORMAL, RESULT_EMPTY, }; /// 여러 라인에 걸친 데이터를 처리하기 위한 상수 enum ParseMode { MODE_NORMAL, MODE_BLOCK_SCALAR, MODE_FOLDED_SCALAR, }; /// \struct STATE 파싱을 위한 상태 구조체 /// \brief YAML 파일 포맷을 보면 알겠지만, 파싱을 위해서 스택이 필요하다. /// 스택에다 가장 최근의 부모를 저장해 두어야, 현재 라인의 인덴트 값과 /// 비교해 부모 자식 관계를 파악할 수 있다. struct STATE { cYamlTree* node; int indent; STATE(cYamlTree* n, int i) : node(n), indent(i) {} }; typedef std::vector STATE_STACK; /// 현재 인덴트 값을 기준으로 스택에서 정확한 부모를 찾아, 주어진 노드를 /// 자식 노드로 편입시킨다. inline cYamlTree* AdoptChild(STATE_STACK& stateStack, int indent, cYamlTree* child) { while (!stateStack.empty()) { STATE& e = stateStack.back(); if (e.indent == indent) return e.node->AddChild(child); else stateStack.pop_back(); } return NULL; } /// 문자열 좌우의 공백을 제거해서 반환한다. inline std::string Trim(std::string str, const std::string& whitespaces = YAML_WHITESPACES) { size_t begin = str.find_first_not_of(whitespaces); size_t end = str.find_last_not_of(whitespaces); if (begin == std::string::npos) { if (end == std::string::npos) return ""; else begin = 0; } else if (end == std::string::npos) { end = str.size(); } return str.substr(begin , end - begin + 1); } /// \brief 주어진 문장에 있는 알파벳을 모두 소문자로 바꾼다. inline std::string Lower(std::string original) { std::transform(original.begin(), original.end(), original.begin(), tolower); return original; } /// \brief 주어진 문자열 내에 존재하는 문자열을 다른 문자열로 치환한다. inline size_t Replace(std::string& text, const std::string& findToken, const std::string& replaceToken) { size_t find_token_length = findToken.size(); size_t replace_token_length = replaceToken.size(); size_t replaced = 0; size_t i = 0; while ((i = text.find(findToken, i)) != std::string::npos) { text.replace(i, find_token_length, replaceToken); i += replace_token_length; ++replaced; } return replaced; } /// \class cYamlStream /// \brief 형식화된 파일 입출력을 위한 스트림 객체. 라인 단위로 파일을 /// 읽어들이는 기능 외에 마지막으로 읽어들인 문자열을 이용해 다음 라인을 /// 어떻게 처리하느냐를 판단하는 기능도 담당한다. class cYamlStream { private: std::ifstream m_File; ///< 스트림 객체 cYamlDocument* m_Document; ///< 도큐먼트 객체 int m_Line; ///< 현재 라인 카운트 int m_Indent; ///< 현재 라인을 파싱해서 얻어낸 인덴트 값 std::string m_Text; ///< 현재 라인 문자열 std::string m_Comment; ///< 현재 처리 중인 코멘트 ParseMode m_Mode; ///< 임시 변수 public: /// 생성자 cYamlStream(const char* fileName, cYamlDocument* document) : m_File(fileName, std::ios::in), m_Document(document), m_Line(0), m_Indent(0) { } /// 소멸자 ~cYamlStream() {} /// 파일에서 한 라인을 읽어들이고, 인덴트 값을 내부 변수에다 기록해둔다. inline ParseResult ParseNextLine(bool processComment) { ++m_Line; char buf[2048+1] = {0, }; m_File.getline(buf, 2048); m_Text = buf; if (m_Text.empty()) return RESULT_EMPTY; // empty line size_t pos = m_Text.find_first_not_of(YAML_INDENT); if (pos == std::string::npos) { m_Comment += "\n"; return RESULT_EMPTY; // only whitespace } if (processComment && m_Text[pos] == YAML_SHARP) { m_Comment += m_Text.substr(pos) + "\n"; return RESULT_EMPTY; // comment line } m_Indent = static_cast(pos); return RESULT_NORMAL; } /// 현재 라인의 key:value 문자열을 이용해 트리 객체를 생성해서 반환한다. inline cYamlTree* CreateTree() { #pragma warning(push) #pragma warning(disable:6211) std::string text(m_Text); cYamlTree* result = NULL; // ':' 문자를 찾는다. size_t pos = text.find_first_of(YAML_COLON); if (pos == std::string::npos) throw std::string("cannot find colon character"); // 파싱 성공! std::string key = Trim(text.substr(0, pos)); std::string value = Trim(text.substr(pos+1)); // 키 문자열이 정상적인지 검사한다. if (key.empty()) throw std::string("key text is empty"); if (key[0] == YAML_MINUS) { m_Indent += static_cast(key.find_first_not_of(YAML_INDENT, 1)); key = Trim(key.substr(1)); result = new cYamlTree("", ""); result->AddChild(new cYamlTree(key, value)); } else { result = new cYamlTree(key, value); } if (!m_Comment.empty()) { // 이때까지 모은 주석을 설정한다. 단 마지막의 '\n' 하나는 뗀다. result->SetComment(m_Comment.substr(0, m_Comment.size() - 1)); m_Comment = ""; } if (value.size() > 1) { if (value[0] == YAML_AMPERSAND) { m_Document->AddAnchor(Trim(value.substr(1)), result); } else if (value[0] == YAML_ASTERISK) { std::string trimmed(value.substr(1)); cYamlTree* anchor = m_Document->GetAnchor(trimmed); if (anchor == NULL) { delete result; throw (std::string("no such anchor ") + trimmed); } result->SetAlias(anchor); } } // 다음 라인을 어떻게 처리할 지 결정해 둔다. if (value == YAML_PIPE) { m_Mode = MODE_BLOCK_SCALAR; result->SetValue(""); } else if (value == YAML_RIGHT_BRACKET) { m_Mode = MODE_FOLDED_SCALAR; result->SetValue(""); } else { m_Mode = MODE_NORMAL; } return result; #pragma warning(pop) } /// 다음 라인을 어떤 식으로 처리해야 하는지를 반환한다. inline ParseMode GetNextMode() const { return m_Mode; } /// 스트림의 상태가 정상적인지의 여부를 반환한다. inline bool IsGood() const { return m_File.good(); } /// 현재 처리 중인 라인의 번호를 반환한다. inline int GetCurrentLine() const { return m_Line; } /// 현재 라인의 인덴트 값을 반환한다. inline int GetIndent() const { return m_Indent; } /// 현재 라인 문자열을 반환한다. inline const std::string& GetText() const { return m_Text; } /// 현재 라인이 빈 라인 또는 코멘트 라인인지의 여부를 반환한다. inline bool IsEmptyLine() const { std::string trimmed(Trim(m_Text)); return trimmed.empty() || trimmed[0] == YAML_SHARP; } }; } cYamlTree::PFN_ERROR_HANDLER cYamlTree::s_ParseErrorHandler = NULL; cYamlTree::PFN_ERROR_HANDLER cYamlTree::s_AccessErrorHandler = NULL; //////////////////////////////////////////////////////////////////////////////// /// \brief 생성자 //////////////////////////////////////////////////////////////////////////////// cYamlTree::cYamlTree() : m_ScalarType(NORMAL_SCALAR), m_Alias(NULL), m_ChildType(CHILD_UNKNOWN), m_MaxChildKeyLength(0) { } //////////////////////////////////////////////////////////////////////////////// /// \brief 생성자 /// \param key 키 /// \param value 값 //////////////////////////////////////////////////////////////////////////////// cYamlTree::cYamlTree(const std::string& key, const std::string& value) : m_Key(key), m_Value(value), m_ScalarType(NORMAL_SCALAR), m_Alias(NULL), m_ChildType(CHILD_UNKNOWN), m_MaxChildKeyLength(0) { } //////////////////////////////////////////////////////////////////////////////// /// \brief 생성자 /// \param key 키 /// \param value 값 //////////////////////////////////////////////////////////////////////////////// cYamlTree::cYamlTree(const std::string& key, const char* value) : m_Key(key), m_Value(value), m_ScalarType(NORMAL_SCALAR), m_Alias(NULL), m_ChildType(CHILD_UNKNOWN), m_MaxChildKeyLength(0) { } //////////////////////////////////////////////////////////////////////////////// /// \brief 생성자 /// \param key 키 /// \param value 값 //////////////////////////////////////////////////////////////////////////////// cYamlTree::cYamlTree(const std::string& key, int value) : m_Key(key), m_ScalarType(NORMAL_SCALAR), m_Alias(NULL), m_ChildType(CHILD_UNKNOWN), m_MaxChildKeyLength(0) { SetValue(value); } //////////////////////////////////////////////////////////////////////////////// /// \brief 생성자 /// \param key 키 /// \param value 값 //////////////////////////////////////////////////////////////////////////////// cYamlTree::cYamlTree(const std::string& key, float value) : m_Key(key), m_ScalarType(NORMAL_SCALAR), m_Alias(NULL), m_ChildType(CHILD_UNKNOWN), m_MaxChildKeyLength(0) { SetValue(value); } //////////////////////////////////////////////////////////////////////////////// /// \brief 생성자 /// \param key 키 /// \param value 값 //////////////////////////////////////////////////////////////////////////////// cYamlTree::cYamlTree(const std::string& key, double value) : m_Key(key), m_ScalarType(NORMAL_SCALAR), m_Alias(NULL), m_ChildType(CHILD_UNKNOWN), m_MaxChildKeyLength(0) { SetValue(value); } //////////////////////////////////////////////////////////////////////////////// /// \brief 생성자 /// \param key 키 /// \param value 값 //////////////////////////////////////////////////////////////////////////////// cYamlTree::cYamlTree(const std::string& key, bool value) : m_Key(key), m_ScalarType(NORMAL_SCALAR), m_Alias(NULL), m_ChildType(CHILD_UNKNOWN), m_MaxChildKeyLength(0) { SetValue(value); } //////////////////////////////////////////////////////////////////////////////// /// \brief 소멸자 //////////////////////////////////////////////////////////////////////////////// cYamlTree::~cYamlTree() { Clear(); } //////////////////////////////////////////////////////////////////////////////// /// \brief 노드의 값을 std::string 반환한다. /// \return const std::string& 값 문자열 //////////////////////////////////////////////////////////////////////////////// const std::string& cYamlTree::GetValue() const { return m_Alias == NULL ? m_Value : m_Alias->GetValue(); } //////////////////////////////////////////////////////////////////////////////// /// \brief 노드의 값을 char* 형태로 반환한다. /// \return const char* 값 문자열 //////////////////////////////////////////////////////////////////////////////// const char* cYamlTree::GetValueAsString() const { return m_Alias == NULL ? m_Value.c_str() : m_Alias->GetValueAsString(); } //////////////////////////////////////////////////////////////////////////////// /// \brief 노드의 값을 int 형태로 변환해서 반환한다. /// \return int 변환한 값 //////////////////////////////////////////////////////////////////////////////// int cYamlTree::GetValueAsInt() const { return m_Alias == NULL ? atoi(GetValueAsString()) : m_Alias->GetValueAsInt(); } //////////////////////////////////////////////////////////////////////////////// /// \brief 노드의 값을 float 형태로 변환해서 반환한다. /// \return float 변환한 값 //////////////////////////////////////////////////////////////////////////////// float cYamlTree::GetValueAsFloat() const { return m_Alias == NULL ? static_cast(atof(GetValueAsString())) : m_Alias->GetValueAsFloat(); } //////////////////////////////////////////////////////////////////////////////// /// \brief 노드의 값을 double 형태로 변환해서 반환한다. /// \return double 변환한 값 //////////////////////////////////////////////////////////////////////////////// double cYamlTree::GetValueAsDouble() const { return m_Alias == NULL ? atof(GetValueAsString()) : m_Alias->GetValueAsDouble(); } //////////////////////////////////////////////////////////////////////////////// /// \brief 노드의 값을 bool 형태로 변환해서 반환한다. /// \return bool 변환한 값. 노드의 값이 대소문자 구별 없이 "true"일 경우에만 /// true가 되고, 그 외에는 모두 false로 취급한다. //////////////////////////////////////////////////////////////////////////////// bool cYamlTree::GetValueAsBool() const { #if _MSC_VER >= 1400 return m_Alias == NULL ? _stricmp(m_Value.c_str(), "true") == 0 : m_Alias->GetValueAsBool(); #else return m_Alias == NULL ? stricmp(m_Value.c_str(), "true") == 0 : m_Alias->GetValueAsBool(); #endif } //////////////////////////////////////////////////////////////////////////////// /// \brief 값 문자열을 설정한다. /// \param value 설정할 값 //////////////////////////////////////////////////////////////////////////////// void cYamlTree::SetValue(const std::string& value) { if (m_Alias == NULL) m_Value = value; else m_Alias->SetValue(value); } //////////////////////////////////////////////////////////////////////////////// /// \brief 값 문자열을 설정한다. /// \param value 설정할 값 //////////////////////////////////////////////////////////////////////////////// void cYamlTree::SetValue(const char* value) { if (m_Alias == NULL) m_Value = value; else m_Alias->SetValue(value); } //////////////////////////////////////////////////////////////////////////////// /// \brief int 형태로 값을 설정한다. /// \param value 설정할 값 //////////////////////////////////////////////////////////////////////////////// void cYamlTree::SetValue(int value) { char buf[256] = {0,}; #if _MSC_VER >= 1400 _snprintf_s(buf, sizeof(buf)-1, _TRUNCATE, "%d", value); #else sprintf(buf, "%d", value); #endif buf[sizeof(buf)-1] = 0; SetValue(buf); } //////////////////////////////////////////////////////////////////////////////// /// \brief float 형태로 값을 설정한다. /// \param value 설정할 값 //////////////////////////////////////////////////////////////////////////////// void cYamlTree::SetValue(float value) { char buf[256] = {0,}; #if _MSC_VER >= 1400 _snprintf_s(buf, sizeof(buf)-1, _TRUNCATE, "%f", value); #else sprintf(buf, "%f", value); #endif buf[sizeof(buf)-1] = 0; SetValue(buf); } //////////////////////////////////////////////////////////////////////////////// /// \brief double 형태로 값을 설정한다. /// \param value 설정할 값 //////////////////////////////////////////////////////////////////////////////// void cYamlTree::SetValue(double value) { char buf[256] = {0,}; #if _MSC_VER >= 1400 _snprintf_s(buf, sizeof(buf)-1, _TRUNCATE, "%f", value); #else sprintf(buf, "%f", value); #endif buf[sizeof(buf)-1] = 0; SetValue(buf); } //////////////////////////////////////////////////////////////////////////////// /// \brief bool 형태로 값을 설정한다. /// \param value 설정할 값 //////////////////////////////////////////////////////////////////////////////// void cYamlTree::SetValue(bool value) { SetValue(value ? "true" : "false"); } //////////////////////////////////////////////////////////////////////////////// /// \brief 주석 문자열을 반환한다. /// \return const std::string& //////////////////////////////////////////////////////////////////////////////// const std::string& cYamlTree::GetComment() const { return m_Alias == NULL ? m_Comment : m_Alias->GetComment(); } //////////////////////////////////////////////////////////////////////////////// /// \brief 주석 문자열을 설정한다. /// \param comment 주석 문자열 //////////////////////////////////////////////////////////////////////////////// void cYamlTree::SetComment(const std::string& comment) { if (m_Alias == NULL) m_Comment = comment; else m_Alias->SetComment(comment); } //////////////////////////////////////////////////////////////////////////////// /// \brief 값의 종류를 반환한다. /// \return cYamlTree::ScalarType 값의 종류 //////////////////////////////////////////////////////////////////////////////// cYamlTree::ScalarType cYamlTree::GetScalarType() const { return m_Alias == NULL ? m_ScalarType : m_Alias->GetScalarType(); } //////////////////////////////////////////////////////////////////////////////// /// \brief 값의 종류를 설정한다. /// \param type 값의 종류 //////////////////////////////////////////////////////////////////////////////// void cYamlTree::SetScalarType(cYamlTree::ScalarType type) { if (m_Alias == NULL) m_ScalarType = type; else m_Alias->SetScalarType(type); } //////////////////////////////////////////////////////////////////////////////// /// \brief 스칼라 값을 추가한다. /// \param value 스칼라 값 //////////////////////////////////////////////////////////////////////////////// void cYamlTree::AddScalar(const std::string& value) { if (m_Alias == NULL) m_Scalars.push_back(value); else m_Alias->AddScalar(value); } //////////////////////////////////////////////////////////////////////////////// /// \brief 스칼라 값의 컬렉션을 반환한다. /// \return const cYamlTree::SCALARS& 스칼라 값의 컬렉션 //////////////////////////////////////////////////////////////////////////////// const cYamlTree::SCALARS& cYamlTree::GetScalars() const { return m_Alias == NULL ? m_Scalars : m_Alias->GetScalars(); } //////////////////////////////////////////////////////////////////////////////// /// \brief 앵커 알리아스를 반환한다. /// \return cYamlTree* 알리아스 //////////////////////////////////////////////////////////////////////////////// cYamlTree* cYamlTree::GetAlias() const { return m_Alias; } //////////////////////////////////////////////////////////////////////////////// /// \brief 앵커 알리아스를 설정한다. /// \param alias 알리아스 //////////////////////////////////////////////////////////////////////////////// void cYamlTree::SetAlias(cYamlTree* alias) { if (alias != this) m_Alias = alias; } //////////////////////////////////////////////////////////////////////////////// /// \brief 자식 노드를 추가한다. 추가한 자식 노드는 내부에서 delete로 /// 삭제하므로 반드시 new를 통해 힙에다 생성해야 한다. /// \param child 추가할 자식 노드 /// \return cYamlTree* 인수로 주어진 child 를 반환한다. //////////////////////////////////////////////////////////////////////////////// cYamlTree* cYamlTree::AddChild(cYamlTree* child) { if (m_Alias) { throw std::string("cannot add child to aliased node"); } // 자식 노드가 있는 경우 스칼라 값을 가질 수 없다. if (!m_Value.empty() && m_Value[0] != YAML_AMPERSAND && m_Value[0] != YAML_ASTERISK) { throw std::string("cannot add child to scalar node"); } // 자식 노드의 key 문자열이 빈 문자열이라면, // 이는 배열을 이용해야 된다는 말이다. if (child->GetKey().empty()) { // 최초의 자식 노드라면 ChildType이 UNKNOWN일 것이다. if (m_ChildType == CHILD_UNKNOWN) m_ChildType = CHILD_SEQNENTIAL; // 최초 이후부터는 같은 타입의 자식 노드만이 들어와야한다. if (m_ChildType != CHILD_SEQNENTIAL) throw std::string("cannot add sequencial node"); char buf[1024] = {0, }; #if _MSC_VER >= 1400 _snprintf_s(buf, sizeof(buf)-1, _TRUNCATE, "%05d", m_SequentialChilds.size()); #else sprintf(buf, "%05d", m_SequentialChilds.size()); #endif buf[sizeof(buf)-1] = 0; m_SequentialChilds.push_back(child); m_MappedChilds.insert(MAPPING::value_type(std::string(buf), child)); return child; } // 자식 노드의 key 문자열이 빈 문자열이 아니라면, // 맵을 이용해서 자식 노드를 관리해야 한다. else { // 최초의 자식 노드라면 ChildType이 UNKNOWN일 것이다. if (m_ChildType == CHILD_UNKNOWN) m_ChildType = CHILD_MAPPED; // 최초 이후부터는 같은 타입의 자식 노드만이 들어와야한다. if (m_ChildType != CHILD_MAPPED) throw std::string("cannot add mapped node"); MAPPING::const_iterator itr(m_MappedChilds.find(Lower(child->GetKey()))); if (itr == m_MappedChilds.end()) { #ifdef NOMINMAX m_MaxChildKeyLength = std::max(m_MaxChildKeyLength, child->GetKey().size()); #else m_MaxChildKeyLength = __max(m_MaxChildKeyLength, child->GetKey().size()); #endif m_MappedChilds.insert(MAPPING::value_type(Lower(child->GetKey()), child)); m_SequentialChilds.push_back(child); return child; } else { char buf[1024] = {0, }; #if _MSC_VER >= 1400 _snprintf_s(buf, sizeof(buf)-1, _TRUNCATE, "cannot add duplicated mapping - %s", child->GetKey().c_str()); #else sprintf(buf, "cannot add duplicated mapping - %s", child->GetKey().c_str()); #endif buf[sizeof(buf)-1] = 0; throw std::string(buf); } } } //////////////////////////////////////////////////////////////////////////////// /// \brief 해당하는 자식 노드를 가지고 있는지의 여부를 반환한다. /// \param key 키 /// \return cYamlTree* 해당하는 키를 가진 자식 노드가 있다면 true를 반환하고, /// 없다면 false를 반환한다. //////////////////////////////////////////////////////////////////////////////// bool cYamlTree::HasChild(const std::string& key) const { if (m_Alias) return m_Alias->HasChild(key); if (m_ChildType == CHILD_MAPPED) { if (m_MappedChilds.find(Lower(key)) != m_MappedChilds.end()) return true; } else if (m_ChildType == CHILD_SEQNENTIAL) { size_t size = m_SequentialChilds.size(); for (size_t i=0; iGetKey() == key) return true; } } return false; } //////////////////////////////////////////////////////////////////////////////// /// \brief 해당하는 인덱스의 자식 노드를 반환한다. /// \param i 인덱스 /// \param strict 해당하는 자식 노드가 존재하지 않을 경우, 어서트를 발생시킬 /// 것인가의 여부 /// \return cYamlTree* 자식 노드 //////////////////////////////////////////////////////////////////////////////// cYamlTree* cYamlTree::GetChild(size_t i, bool strict) const { if (m_Alias) return m_Alias->GetChild(i, strict); if (i < m_SequentialChilds.size()) return m_SequentialChilds[i]; if (strict) { char buf[1024] = {0, }; #if _MSC_VER >= 1400 _snprintf_s(buf, sizeof(buf)-1, _TRUNCATE, "out of range in node %s with %d", m_Key.c_str(), i); #else sprintf(buf, "out of range in node %s with %d", m_Key.c_str(), i); #endif buf[sizeof(buf)-1] = 0; throw std::string(buf); } return NULL; } //////////////////////////////////////////////////////////////////////////////// /// \brief 해당하는 인덱스의 자식 노드를 반환한다. /// \param i 인덱스 /// \return const cYamlTree& 해당하는 인덱스의 자식 노드. 범위를 넘어간 경우 /// 어디를 가르키게 될지는 모른다. //////////////////////////////////////////////////////////////////////////////// cYamlTree& cYamlTree::operator[](size_t i) { return *GetChild(i, true); } //////////////////////////////////////////////////////////////////////////////// /// \brief 해당하는 인덱스의 자식 노드를 반환한다. /// \param i 인덱스 /// \return const cYamlTree& 해당하는 인덱스의 자식 노드. 범위를 넘어간 경우 /// 어디를 가르키게 될지는 모른다. //////////////////////////////////////////////////////////////////////////////// const cYamlTree& cYamlTree::operator[](size_t i) const { return *GetChild(i, true); } //////////////////////////////////////////////////////////////////////////////// /// \brief 자식 노드의 갯수를 반환한다. /// \return size_t 자식 노드의 갯수 //////////////////////////////////////////////////////////////////////////////// size_t cYamlTree::GetChildCount() const { if (m_Alias) return m_Alias->GetChildCount(); if (m_ChildType == CHILD_MAPPED) return m_MappedChilds.size(); if (m_ChildType == CHILD_SEQNENTIAL) return m_SequentialChilds.size(); return 0; } //////////////////////////////////////////////////////////////////////////////// /// \brief 해당하는 키를 가진 자식 노드를 반환한다. /// \param key 키 /// \param strict 해당하는 자식 노드가 존재하지 않을 경우, 어서트를 발생시킬 /// 것인가의 여부 /// \return cYamlTree* 해당하는 키를 가진 자식 노드가 있다면 그 노드의 포인터를 /// 반환하고, 없다면 NULL을 반환한다. //////////////////////////////////////////////////////////////////////////////// cYamlTree* cYamlTree::GetChild(const std::string& key, bool strict) const { if (m_Alias) return m_Alias->GetChild(key, strict); if (m_ChildType == CHILD_MAPPED) { MAPPING::const_iterator itr(m_MappedChilds.find(Lower(key))); if (itr != m_MappedChilds.end()) return itr->second; if (strict) { char buf[1024] = {0, }; #if _MSC_VER >= 1400 _snprintf_s(buf, sizeof(buf)-1, _TRUNCATE, "cannot find specified child node %s at %s", key.c_str(), m_Key.c_str()); #else sprintf(buf, "cannot find specified child node %s at %s", key.c_str(), m_Key.c_str()); #endif buf[sizeof(buf)-1] = 0; if (s_AccessErrorHandler) s_AccessErrorHandler(buf); else throw std::string(buf); } return NULL; } else if (m_ChildType == CHILD_SEQNENTIAL) { size_t size = m_SequentialChilds.size(); for (size_t i=0; iGetKey() == key) return child; } if (strict) { char buf[1024] = {0, }; #if _MSC_VER >= 1400 _snprintf_s(buf, sizeof(buf)-1, _TRUNCATE, "cannot find specified child node %s at %s", key.c_str(), m_Key.c_str()); #else sprintf(buf, "cannot find specified child node %s at %s", key.c_str(), m_Key.c_str()); #endif buf[sizeof(buf)-1] = 0; if (s_AccessErrorHandler) s_AccessErrorHandler(buf); else throw std::string(buf); } return NULL; } if (strict) { char buf[1024] = {0, }; #if _MSC_VER >= 1400 _snprintf_s(buf, sizeof(buf)-1, _TRUNCATE, "cannot find specified child node %s at %s", key.c_str(), m_Key.c_str()); #else sprintf(buf, "cannot find specified child node %s at %s", key.c_str(), m_Key.c_str()); #endif buf[sizeof(buf)-1] = 0; if (s_AccessErrorHandler) s_AccessErrorHandler(buf); else throw std::string(buf); } return NULL; } //////////////////////////////////////////////////////////////////////////////// /// \brief 해당하는 키를 가진 자식 노드 중에 첫번째 것을 반환한다. /// \param key 키 /// \return const cYamlTree& 해당하는 키를 가진 자식 노드. 해당하는 자식 노드가 /// 없을 경우에는 어떻게 될 지 모른다. //////////////////////////////////////////////////////////////////////////////// cYamlTree& cYamlTree::operator[](const std::string& key) { return *GetChild(key, true); } //////////////////////////////////////////////////////////////////////////////// /// \brief 해당하는 키를 가진 자식 노드 중에 첫번째 것을 반환한다. /// \param key 키 /// \return const cYamlTree& 해당하는 키를 가진 자식 노드. 해당하는 자식 노드가 /// 없을 경우에는 어떻게 될 지 모른다. //////////////////////////////////////////////////////////////////////////////// const cYamlTree& cYamlTree::operator[](const std::string& key) const { return *GetChild(key, true); } //////////////////////////////////////////////////////////////////////////////// /// \brief 해당하는 키를 가진 자식 노드를 찾아, 그 노드의 텍스트 값을 /// const char* 형태로 반환한다. /// \param key 키 /// \return const char* 해당하는 키를 가진 자식 노드가 있다면 그 노드의 값을 /// 반환하고, 없다면 빈 문자열을 반환한다. /// \note 해당하는 자식 노드가 없는 경우, 디버그 버전에서는 어서트를 발생시킨다. //////////////////////////////////////////////////////////////////////////////// const char* cYamlTree::AttrAsString(const std::string& key) const { cYamlTree* child = GetChild(key, true); return child ? child->GetValueAsString() : ""; } //////////////////////////////////////////////////////////////////////////////// /// \brief 해당하는 키를 가진 자식 노드를 찾아, 그 노드의 텍스트 값을 /// int 형태로 반환한다. /// \param key 키 /// \return int 해당하는 키를 가진 자식 노드가 있다면 그 노드의 값을 /// 반환하고, 없다면 -1을 반환한다. /// \note 해당하는 자식 노드가 없는 경우, 디버그 버전에서는 어서트를 발생시킨다. //////////////////////////////////////////////////////////////////////////////// int cYamlTree::AttrAsInt(const std::string& key) const { cYamlTree* child = GetChild(key, true); return child ? child->GetValueAsInt() : -1; } //////////////////////////////////////////////////////////////////////////////// /// \brief 해당하는 키를 가진 자식 노드를 찾아, 그 노드의 텍스트 값을 /// float 형태로 반환한다. /// \param key 키 /// \return float 해당하는 키를 가진 자식 노드가 있다면 그 노드의 값을 /// 반환하고, 없다면 -1.0f를 반환한다. /// \note 해당하는 자식 노드가 없는 경우, 디버그 버전에서는 어서트를 발생시킨다. //////////////////////////////////////////////////////////////////////////////// float cYamlTree::AttrAsFloat(const std::string& key) const { cYamlTree* child = GetChild(key, true); return child ? child->GetValueAsFloat() : -1.0f; } //////////////////////////////////////////////////////////////////////////////// /// \brief 해당하는 키를 가진 자식 노드를 찾아, 그 노드의 텍스트 값을 double /// 형태로 반환한다. /// \param key 키 /// \return double 해당하는 키를 가진 자식 노드가 있다면 그 노드의 값을 /// 반환하고, 없다면 -1.0을 반환한다. /// \note 해당하는 자식 노드가 없는 경우, 디버그 버전에서는 어서트를 발생시킨다. //////////////////////////////////////////////////////////////////////////////// double cYamlTree::AttrAsDouble(const std::string& key) const { cYamlTree* child = GetChild(key, true); return child ? child->GetValueAsDouble() : -1.0; } //////////////////////////////////////////////////////////////////////////////// /// \brief 해당하는 키를 가진 자식 노드를 찾아, 그 노드의 텍스트 값을 /// bool 형태로 반환한다. /// \param key 키 /// \return bool 해당하는 키를 가진 자식 노드가 있다면 그 노드의 값을 /// 반환하고, 없다면 false를 반환한다. /// \note 해당하는 자식 노드가 없는 경우, 디버그 버전에서는 어서트를 발생시킨다. //////////////////////////////////////////////////////////////////////////////// bool cYamlTree::AttrAsBool(const std::string& key) const { cYamlTree* child = GetChild(key, true); return child ? child->GetValueAsBool() : false; } //////////////////////////////////////////////////////////////////////////////// /// \brief 해당하는 키를 가진 자식 노드를 찾아, 그 노드의 텍스트 값을 /// const char* 형태로 반환한다. /// \param key 키 /// \param nullValue 해당하는 자식 노드가 존재하지 않을 경우에 반환할 값 /// \return const char* 해당하는 키를 가진 자식 노드가 있다면 그 노드의 값을 /// 반환하고, 없다면 주어진 nullValue를 반환한다. //////////////////////////////////////////////////////////////////////////////// const char* cYamlTree::AttrAsStringSafe(const std::string& key, const char* nullValue) const { cYamlTree* child = GetChild(key, false); return child ? child->GetValueAsString() : nullValue; } //////////////////////////////////////////////////////////////////////////////// /// \brief 해당하는 키를 가진 자식 노드를 찾아, 그 노드의 텍스트 값을 /// int 형태로 반환한다. /// \param key 키 /// \param nullValue 해당하는 자식 노드가 존재하지 않을 경우에 반환할 값 /// \return int 해당하는 키를 가진 자식 노드가 있다면 그 노드의 값을 /// 반환하고, 없다면 주어진 nullValue를 반환한다. //////////////////////////////////////////////////////////////////////////////// int cYamlTree::AttrAsIntSafe(const std::string& key, int nullValue) const { cYamlTree* child = GetChild(key, false); return child ? child->GetValueAsInt() : nullValue; } //////////////////////////////////////////////////////////////////////////////// /// \brief 해당하는 키를 가진 자식 노드를 찾아, 그 노드의 텍스트 값을 /// float 형태로 반환한다. /// \param key 키 /// \param nullValue 해당하는 자식 노드가 존재하지 않을 경우에 반환할 값 /// \return float 해당하는 키를 가진 자식 노드가 있다면 그 노드의 값을 /// 반환하고, 없다면 주어진 nullValue를 반환한다. //////////////////////////////////////////////////////////////////////////////// float cYamlTree::AttrAsFloatSafe(const std::string& key, float nullValue) const { cYamlTree* child = GetChild(key, false); return child ? child->GetValueAsFloat() : nullValue; } //////////////////////////////////////////////////////////////////////////////// /// \brief 해당하는 키를 가진 자식 노드를 찾아, 그 노드의 텍스트 값을 /// double 형태로 반환한다. /// \param key 키 /// \param nullValue 해당하는 자식 노드가 존재하지 않을 경우에 반환할 값 /// \return double 해당하는 키를 가진 자식 노드가 있다면 그 노드의 값을 /// 반환하고, 없다면 주어진 nullValue를 반환한다. //////////////////////////////////////////////////////////////////////////////// double cYamlTree::AttrAsDoubleSafe(const std::string& key, double nullValue) const { cYamlTree* child = GetChild(key, false); return child ? child->GetValueAsDouble() : nullValue; } //////////////////////////////////////////////////////////////////////////////// /// \brief 해당하는 키를 가진 자식 노드를 찾아, 그 노드의 텍스트 값을 /// bool 형태로 반환한다. /// \param key 키 /// \param nullValue 해당하는 자식 노드가 존재하지 않을 경우에 반환할 값 /// \return bool 해당하는 키를 가진 자식 노드가 있다면 그 노드의 값을 /// 반환하고, 없다면 주어진 nullValue를 반환한다. //////////////////////////////////////////////////////////////////////////////// bool cYamlTree::AttrAsBoolSafe(const std::string& key, bool nullValue) const { cYamlTree* child = GetChild(key, false); return child ? child->GetValueAsBool() : nullValue; } //////////////////////////////////////////////////////////////////////////////// /// \brief 자식 노드를 추가한다. /// \param key 키 /// \param value 값 /// \return cYamlTree* 추가한 자식 노드의 포인터 //////////////////////////////////////////////////////////////////////////////// cYamlTree* cYamlTree::AddAttr(const std::string& key, const std::string& value) { return AddChild(new cYamlTree(key, value)); } //////////////////////////////////////////////////////////////////////////////// /// \brief 자식 노드를 추가한다. /// \param key 키 /// \param value 값 /// \return cYamlTree* 추가한 자식 노드의 포인터 //////////////////////////////////////////////////////////////////////////////// cYamlTree* cYamlTree::AddAttr(const std::string& key, const char* value) { return AddChild(new cYamlTree(key, value)); } //////////////////////////////////////////////////////////////////////////////// /// \brief 자식 노드를 추가한다. /// \param key 키 /// \param value 값 /// \return cYamlTree* 추가한 자식 노드의 포인터 //////////////////////////////////////////////////////////////////////////////// cYamlTree* cYamlTree::AddAttr(const std::string& key, int value) { return AddChild(new cYamlTree(key, value)); } //////////////////////////////////////////////////////////////////////////////// /// \brief 자식 노드를 추가한다. /// \param key 키 /// \param value 값 /// \return cYamlTree* 추가한 자식 노드의 포인터 //////////////////////////////////////////////////////////////////////////////// cYamlTree* cYamlTree::AddAttr(const std::string& key, float value) { return AddChild(new cYamlTree(key, value)); } //////////////////////////////////////////////////////////////////////////////// /// \brief 자식 노드를 추가한다. /// \param key 키 /// \param value 값 /// \return cYamlTree* 추가한 자식 노드의 포인터 //////////////////////////////////////////////////////////////////////////////// cYamlTree* cYamlTree::AddAttr(const std::string& key, double value) { return AddChild(new cYamlTree(key, value)); } //////////////////////////////////////////////////////////////////////////////// /// \brief 자식 노드를 추가한다. /// \param key 키 /// \param value 값 /// \return cYamlTree* 추가한 자식 노드의 포인터 //////////////////////////////////////////////////////////////////////////////// cYamlTree* cYamlTree::AddAttr(const std::string& key, bool value) { return AddChild(new cYamlTree(key, value)); } //////////////////////////////////////////////////////////////////////////////// /// \brief 자신 및 모든 자식 노드들을 초기화한다. //////////////////////////////////////////////////////////////////////////////// void cYamlTree::Clear() { if (m_SequentialChilds.size() != m_MappedChilds.size()) { char buf[1024] = {0, }; #if _MSC_VER >= 1400 _snprintf_s(buf, sizeof(buf)-1, _TRUNCATE, "child count mismatch at node %s", m_Key.c_str()); #else sprintf(buf, "child count mismatch at node %s", m_Key.c_str()); #endif buf[sizeof(buf)-1] = 0; if (s_AccessErrorHandler) s_AccessErrorHandler(buf); else throw std::string(buf); } m_Key = ""; m_Value = ""; m_Comment = ""; m_ScalarType = NORMAL_SCALAR; m_Scalars.clear(); m_Alias = NULL; m_ChildType = CHILD_UNKNOWN; m_MaxChildKeyLength = 0; size_t size = m_SequentialChilds.size(); for (size_t i=0; iToString(0, mapped, "", beautify, m_MaxChildKeyLength); } return msg.str(); } //////////////////////////////////////////////////////////////////////////////// /// \brief 내부 데이터를 문자열 형태로 반환한다. /// \param indent 들여쓰기할 칸 /// \param mapped 부모 노드의 child type /// \param header 키 문자열 출력시 앞에다 붙일 헤더 /// \param beautify 출력 파일의 인덴트를 예쁘게 정렬할 것인가의 여부 /// \param maxKeyLength 인덴트를 예쁘게 하는데 사용할 값 /// \return std::string 문자열 형태로 변환한 데이터 //////////////////////////////////////////////////////////////////////////////// std::string cYamlTree::ToString(size_t indent, bool mapped, const std::string& header, bool beautify, size_t maxKeyLength) const { std::stringstream msg; std::string spaces; for (size_t i=0; i" << std::endl; for (SCALARS::const_iterator itr(m_Scalars.begin()); itr != m_Scalars.end(); ++itr) { msg << spaces << " " << *itr << std::endl; } break; default: msg << spaces << m_Key << ": " << value << std::endl; break; } if (m_ChildType == CHILD_MAPPED) { size_t size = m_SequentialChilds.size(); for (size_t i=0; iToString(indent + 2, true, "", beautify, m_MaxChildKeyLength); else msg << child->ToString(indent + 3, true, "", beautify, m_MaxChildKeyLength); } } else if (m_ChildType == CHILD_SEQNENTIAL) { size_t size = m_SequentialChilds.size(); for (size_t i=0; iToString(indent + 1, true, "", beautify, m_MaxChildKeyLength); else msg << child->ToString(indent + 2, true, "", beautify, m_MaxChildKeyLength); } } } else { bool first = true; bool childMapped = m_ChildType == CHILD_MAPPED; size_t size = m_SequentialChilds.size(); for (size_t i=0; iToString(indent, childMapped, "- ", beautify, m_MaxChildKeyLength); first = false; } else { msg << child->ToString(indent + 1, childMapped, "", beautify, m_MaxChildKeyLength); } } } return msg.str(); } //////////////////////////////////////////////////////////////////////////////// /// \brief 생성자 //////////////////////////////////////////////////////////////////////////////// cYamlDocument::cYamlDocument() : cYamlTree() { } //////////////////////////////////////////////////////////////////////////////// /// \brief 소멸자 //////////////////////////////////////////////////////////////////////////////// cYamlDocument::~cYamlDocument() { } //////////////////////////////////////////////////////////////////////////////// /// \brief 앵커를 추가한다. /// \param name 앵커 이름 /// \param tree 앵커의 대상이 되는 트리 //////////////////////////////////////////////////////////////////////////////// void cYamlDocument::AddAnchor(const std::string& name, cYamlTree* tree) { if (!name.empty()) m_Anchors[name] = tree; } //////////////////////////////////////////////////////////////////////////////// /// \brief 해당하는 이름의 앵커를 반환한다. /// \param name 앵커 이름 /// \return cYamlTree* 해당하는 트리가 존재하는 경우 그 트리를 반환하고, /// 존재하지 않는 경우에는 NULL을 반환한다. //////////////////////////////////////////////////////////////////////////////// cYamlTree* cYamlDocument::GetAnchor(const std::string& name) const { ANCHORS::const_iterator itr(m_Anchors.find(name)); return itr != m_Anchors.end() ? itr->second : NULL; } //////////////////////////////////////////////////////////////////////////////// /// \brief 모든 앵커를 삭제한다. //////////////////////////////////////////////////////////////////////////////// void cYamlDocument::ClearAnchors() { m_Anchors.clear(); } //////////////////////////////////////////////////////////////////////////////// /// \brief YAML 파일에서 데이터를 읽어들여 데이터를 구성한다. /// \param fileName 파일 이름 /// \return bool 파일을 무사히 읽어들인 경우 true, 뭔가 에러가 생긴 경우에는 /// false를 반환한다. 에러가 생긴 경우에는 std::cerr을 이용해 에러 메시지가 /// 출력된다. //////////////////////////////////////////////////////////////////////////////// bool cYamlDocument::Load(const char* fileName) { Clear(); ClearAnchors(); cYamlStream stream(fileName, this); if (!stream.IsGood()) { char buf[1024] = {0,}; #if _MSC_VER >= 1400 _snprintf_s(buf, sizeof(buf)-1, _TRUNCATE, "cannot open %s", fileName); #else sprintf(buf, "cannot open %s", fileName); #endif buf[sizeof(buf)-1] = 0; m_LastError = buf; return false; } std::vector stateStack; stateStack.push_back(STATE(this, -1)); bool result = true; ParseMode mode = MODE_NORMAL; cYamlTree* lastChild = NULL; try { while (stream.IsGood()) { // 스택이 비었다는 말은, AdoptChild 함수에서 // 올바른 parent를 찾다가 결국 못 찾았다는 이야기다. // 그러므로 이는 인덴트 에러다. if (stateStack.empty()) throw std::string("indent error"); if (stream.ParseNextLine(mode == MODE_NORMAL) != RESULT_NORMAL) continue; // 주석이라면 패스~ int indent = stream.GetIndent(); STATE& top = stateStack.back(); // YAML은 무조건 컬럼 0부터 시작할 필요가 없기 때문에, // 루트 노드가 컬럼 0에 존재한다고 가정하면 곤란하다. // 제일 처음에 노드가 나오는 컬럼을 루트 노드의 컬럼으로 잡는다. if (top.indent == -1) { top.indent = indent; } //------------------------------------------------------------------ // 한 라인씩 처리 중 //------------------------------------------------------------------ if (mode == MODE_NORMAL) { if (top.indent == indent) // 탭이 이전 노드와 같다. { //cYamlTree* child = stream.CreateTree(); //lastChild = top.node->AddChild(child); /// 부모 노드가 alias된 노드라면 곤란하다. if (top.node->GetAlias()) { std::string msg("cannot add child node to aliased node "); throw (msg + top.node->GetKey()); } cYamlTree* child = stream.CreateTree(); if (child->GetChildCount() == 0 || child->GetAlias()) { lastChild = top.node->AddChild(child); } else { top.node->AddChild(child); stateStack.push_back(STATE(child, stream.GetIndent())); lastChild = child->GetChild(0); } } else if (top.indent < indent) // 탭이 늘어났다. { if (lastChild == NULL) throw std::string("indent error"); /// 부모 노드가 alias된 노드라면 곤란하다. if (lastChild->GetAlias()) { std::string msg("cannot add child node to aliased node "); throw (msg + lastChild->GetKey()); } stateStack.push_back(STATE(lastChild, indent)); cYamlTree* child = stream.CreateTree(); if (child->GetChildCount() == 0 || child->GetAlias()) { lastChild = lastChild->AddChild(child); } else { stateStack.push_back(STATE(child, stream.GetIndent())); lastChild->AddChild(child); lastChild = child->GetChild(0); } } else if (indent < top.indent) // 탭이 줄어들었다. { cYamlTree* child = stream.CreateTree(); if (child->GetChildCount() == 0 || child->GetAlias()) { lastChild = AdoptChild(stateStack, indent, child); } else { AdoptChild(stateStack, indent, child); stateStack.push_back(STATE(child, stream.GetIndent())); lastChild = child->GetChild(0); } } mode = stream.GetNextMode(); } //------------------------------------------------------------------ // 여러 행에 걸친 값 처리 중 //------------------------------------------------------------------ else { if (top.indent == indent) // 탭이 이전 노드와 같다. { if (stream.IsEmptyLine()) continue; cYamlTree* child = stream.CreateTree(); lastChild = top.node->AddChild(child); mode = stream.GetNextMode(); } else if (top.indent < indent) // 탭이 늘어났다. { std::string trimmed(Trim(stream.GetText())); if (mode == MODE_BLOCK_SCALAR) { lastChild->SetValue(lastChild->GetValue() + "\n" + trimmed); lastChild->SetScalarType(cYamlTree::BLOCK_SCALAR); lastChild->AddScalar(trimmed); } else if (mode == MODE_FOLDED_SCALAR) { lastChild->SetValue(lastChild->GetValue() + " " + trimmed); lastChild->SetScalarType(cYamlTree::FOLDED_SCALAR); lastChild->AddScalar(trimmed); } } else if (indent < top.indent) // 탭이 줄어들었다. { // 탭이 줄어들었다고 해도, 커멘트 라인이라면 패스 if (stream.IsEmptyLine()) continue; lastChild = AdoptChild(stateStack, indent, stream.CreateTree()); mode = stream.GetNextMode(); } } } } catch (std::string& e) { char buf[1024] = {0, }; #if _MSC_VER >= 1400 _snprintf_s(buf, sizeof(buf), _TRUNCATE, "%s:%d: %s", fileName, stream.GetCurrentLine(), e.c_str()); #else sprintf(buf, "%s:%d: %s", fileName, stream.GetCurrentLine(), e.c_str()); #endif buf[sizeof(buf)-1] = 0; m_LastError = buf; if (s_ParseErrorHandler) s_ParseErrorHandler(m_LastError.c_str()); result = false; } return result; } //////////////////////////////////////////////////////////////////////////////// /// \brief 내부 데이터를 파일에다 저장한다. /// \param fileName 파일 이름 /// \param beautify 출력 파일의 인덴트를 예쁘게 정렬할 것인가의 여부 /// \return bool 저장에 성공한 경우 true, 파일을 쓰기용으로 열지 못했다면 false //////////////////////////////////////////////////////////////////////////////// bool cYamlDocument::Save(const char* fileName, bool beautify) const { std::ofstream file(fileName, std::ios::out | std::ios::trunc); if (!file) return false; file << ToString(beautify, false) << std::endl; return true; } #ifdef D3DX_VERSION //////////////////////////////////////////////////////////////////////////////// /// \brief 생성자 /// \param key 키 /// \param value 값 //////////////////////////////////////////////////////////////////////////////// cYamlTree::cYamlTree(const std::string& key, const D3DXVECTOR2& value) : m_Key(key), m_ScalarType(NORMAL_SCALAR), m_Alias(NULL), m_ChildType(CHILD_UNKNOWN), m_MaxChildKeyLength(0) { SetValue(value); } //////////////////////////////////////////////////////////////////////////////// /// \brief 생성자 /// \param key 키 /// \param value 값 //////////////////////////////////////////////////////////////////////////////// cYamlTree::cYamlTree(const std::string& key, const D3DXVECTOR3& value) : m_Key(key), m_ScalarType(NORMAL_SCALAR), m_Alias(NULL), m_ChildType(CHILD_UNKNOWN), m_MaxChildKeyLength(0) { SetValue(value); } //////////////////////////////////////////////////////////////////////////////// /// \brief 생성자 /// \param key 키 /// \param value 값 //////////////////////////////////////////////////////////////////////////////// cYamlTree::cYamlTree(const std::string& key, const D3DXVECTOR4& value) : m_Key(key), m_ScalarType(NORMAL_SCALAR), m_Alias(NULL), m_ChildType(CHILD_UNKNOWN), m_MaxChildKeyLength(0) { SetValue(value); } //////////////////////////////////////////////////////////////////////////////// /// \brief 생성자 /// \param key 키 /// \param value 값 //////////////////////////////////////////////////////////////////////////////// cYamlTree::cYamlTree(const std::string& key, const D3DXQUATERNION& value) : m_Key(key), m_ScalarType(NORMAL_SCALAR), m_Alias(NULL), m_ChildType(CHILD_UNKNOWN), m_MaxChildKeyLength(0) { SetValue(value); } //////////////////////////////////////////////////////////////////////////////// /// \brief 생성자 /// \param key 키 /// \param value 값 //////////////////////////////////////////////////////////////////////////////// cYamlTree::cYamlTree(const std::string& key, const D3DXCOLOR& value) : m_Key(key), m_ScalarType(NORMAL_SCALAR), m_Alias(NULL), m_ChildType(CHILD_UNKNOWN), m_MaxChildKeyLength(0) { SetValue(value); } //////////////////////////////////////////////////////////////////////////////// /// \brief 노드의 값을 D3DXVECTOR2 형태로 반환한다. /// \return D3DXVECTOR2 값 //////////////////////////////////////////////////////////////////////////////// D3DXVECTOR2 cYamlTree::GetValueAsVector2() const { char text[256] = {0, }; #if _MSC_VER >= 1400 strcpy_s(text, sizeof(text), m_Value.c_str()); #else strcpy(text, m_Value.c_str()); #endif float value[2] = {0.0f, }; int count = 0; char* token = NULL; #if _MSC_VER >= 1400 char* context = NULL; token = strtok_s(text, ", \t", &context); #else token = strtok(text, ", \t"); #endif while (token && count < 2) { value[count++] = static_cast(atof(token)); #if _MSC_VER >= 1400 token = strtok_s(NULL, ", \t", &context); #else token = strtok(text, ", \t"); #endif } return D3DXVECTOR2(value); } //////////////////////////////////////////////////////////////////////////////// /// \brief 노드의 값을 D3DXVECTOR3 형태로 반환한다. /// \return D3DXVECTOR3 값 //////////////////////////////////////////////////////////////////////////////// D3DXVECTOR3 cYamlTree::GetValueAsVector3() const { char text[256] = {0, }; #if _MSC_VER >= 1400 strcpy_s(text, sizeof(text), m_Value.c_str()); #else strcpy(text, m_Value.c_str()); #endif float value[3] = {0.0f, }; int count = 0; char* token = NULL; #if _MSC_VER >= 1400 char* context = NULL; token = strtok_s(text, ", \t", &context); #else token = strtok(text, ", \t"); #endif while (token && count < 3) { value[count++] = static_cast(atof(token)); #if _MSC_VER >= 1400 token = strtok_s(NULL, ", \t", &context); #else token = strtok(text, ", \t"); #endif } return D3DXVECTOR3(value); } //////////////////////////////////////////////////////////////////////////////// /// \brief 노드의 값을 D3DXVECTOR4 형태로 반환한다. /// \return D3DXVECTOR4 값 //////////////////////////////////////////////////////////////////////////////// D3DXVECTOR4 cYamlTree::GetValueAsVector4() const { char text[256] = {0, }; #if _MSC_VER >= 1400 strcpy_s(text, sizeof(text), m_Value.c_str()); #else strcpy(text, m_Value.c_str()); #endif float value[4] = {0.0f, }; int count = 0; char* token = NULL; #if _MSC_VER >= 1400 char* context = NULL; token = strtok_s(text, ", \t", &context); #else token = strtok(text, ", \t"); #endif while (token && count < 4) { value[count++] = static_cast(atof(token)); #if _MSC_VER >= 1400 token = strtok_s(NULL, ", \t", &context); #else token = strtok(text, ", \t"); #endif } return D3DXVECTOR4(value); } //////////////////////////////////////////////////////////////////////////////// /// \brief 노드의 값을 D3DXQUATERNION 형태로 반환한다. /// \return D3DXQUATERNION 값 //////////////////////////////////////////////////////////////////////////////// D3DXQUATERNION cYamlTree::GetValueAsQuaternion() const { char text[256] = {0, }; #if _MSC_VER >= 1400 strcpy_s(text, sizeof(text), m_Value.c_str()); #else strcpy(text, m_Value.c_str()); #endif float value[4] = {0.0f, }; int count = 0; char* token = NULL; #if _MSC_VER >= 1400 char* context = NULL; token = strtok_s(text, ", \t", &context); #else token = strtok(text, ", \t"); #endif while (token && count < 4) { value[count++] = static_cast(atof(token)); #if _MSC_VER >= 1400 token = strtok_s(NULL, ", \t", &context); #else token = strtok(text, ", \t"); #endif } return D3DXQUATERNION(value); } //////////////////////////////////////////////////////////////////////////////// /// \brief 노드의 값을 D3DXCOLOR 형태로 반환한다. /// \return D3DXCOLOR 값 //////////////////////////////////////////////////////////////////////////////// D3DXCOLOR cYamlTree::GetValueAsColor() const { char text[256] = {0, }; #if _MSC_VER >= 1400 strcpy_s(text, sizeof(text), m_Value.c_str()); #else strcpy(text, m_Value.c_str()); #endif unsigned char value[4] = {0, }; int count = 0; char* token = NULL; #if _MSC_VER >= 1400 char* context = NULL; token = strtok_s(text, ", \t", &context); #else token = strtok(text, ", \t"); #endif while (token && count < 4) { value[count++] = (unsigned char)atoi(token); #if _MSC_VER >= 1400 token = strtok_s(NULL, ", \t", &context); #else token = strtok(text, ", \t"); #endif } return D3DXCOLOR(D3DCOLOR_RGBA(value[0], value[1], value[2], value[3])); } //////////////////////////////////////////////////////////////////////////////// /// \brief D3DXVECTOR2 형태로 값을 설정한다. /// \param value 설정할 값 //////////////////////////////////////////////////////////////////////////////// void cYamlTree::SetValue(const D3DXVECTOR2& value) { char buf[256] = {0, }; #if _MSC_VER >= 1400 _snprintf_s(buf, sizeof(buf)-1, _TRUNCATE, "%f, %f", value.x, value.y); #else sprintf(buf, "%f, %f", value.x, value.y); #endif buf[sizeof(buf)-1] = 0; SetValue(buf); } //////////////////////////////////////////////////////////////////////////////// /// \brief D3DXVECTOR3 형태로 값을 설정한다. /// \param value 설정할 값 //////////////////////////////////////////////////////////////////////////////// void cYamlTree::SetValue(const D3DXVECTOR3& value) { char buf[256] = {0, }; #if _MSC_VER >= 1400 _snprintf_s(buf, sizeof(buf)-1, _TRUNCATE, "%f, %f, %f", value.x, value.y, value.z); #else sprintf(buf, "%f, %f, %f", value.x, value.y, value.z); #endif buf[sizeof(buf)-1] = 0; SetValue(buf); } //////////////////////////////////////////////////////////////////////////////// /// \brief D3DXVECTOR4 형태로 값을 설정한다. /// \param value 설정할 값 //////////////////////////////////////////////////////////////////////////////// void cYamlTree::SetValue(const D3DXVECTOR4& value) { char buf[256] = {0, }; #if _MSC_VER >= 1400 _snprintf_s(buf, sizeof(buf)-1, _TRUNCATE, "%f, %f, %f, %f", value.x, value.y, value.z, value.w); #else sprintf(buf, "%f, %f, %f, %f", value.x, value.y, value.z, value.w); #endif buf[sizeof(buf)-1] = 0; SetValue(buf); } //////////////////////////////////////////////////////////////////////////////// /// \brief D3DXQUATERNION 형태로 값을 설정한다. /// \param value 설정할 값 //////////////////////////////////////////////////////////////////////////////// void cYamlTree::SetValue(const D3DXQUATERNION& value) { char buf[256] = {0, }; #if _MSC_VER >= 1400 _snprintf_s(buf, sizeof(buf)-1, _TRUNCATE, "%f, %f, %f, %f", value.x, value.y, value.z, value.w); #else sprintf(buf, "%f, %f, %f, %f", value.x, value.y, value.z, value.w); #endif buf[sizeof(buf)-1] = 0; SetValue(buf); } //////////////////////////////////////////////////////////////////////////////// /// \brief D3DXCOLOR 형태로 값을 설정한다. /// \param value 설정할 값 //////////////////////////////////////////////////////////////////////////////// void cYamlTree::SetValue(const D3DXCOLOR& value) { char buf[256] = {0, }; #if _MSC_VER >= 1400 _snprintf_s(buf, sizeof(buf)-1, _TRUNCATE, "%d, %d, %d, %d", (int)(value.r * 255), (int)(value.g * 255), (int)(value.b * 255), (int)(value.a * 255)); #else sprintf(buf, "%d, %d, %d, %d", (int)(value.r * 255), (int)(value.g * 255), (int)(value.b * 255), (int)(value.a * 255)); #endif buf[sizeof(buf)-1] = 0; SetValue(buf); } //////////////////////////////////////////////////////////////////////////////// /// \brief 해당하는 키를 가진 자식 노드를 찾아, 그 노드의 텍스트 값을 /// D3DXVECTOR2 형태로 반환한다. /// \param key 키 /// \return D3DXVECTOR2 해당하는 키를 가진 자식 노드가 있다면 그 노드의 값을 /// 반환하고, 없다면 0.0f 배열을 반환한다. /// \note 해당하는 자식 노드가 없는 경우, 디버그 버전에서는 어서트를 발생시킨다. //////////////////////////////////////////////////////////////////////////////// D3DXVECTOR2 cYamlTree::AttrAsVector2(const std::string& key) const { cYamlTree* child = GetChild(key, true); return child ? child->GetValueAsVector2() : D3DXVECTOR2(0.0f, 0.0f); } //////////////////////////////////////////////////////////////////////////////// /// \brief 해당하는 키를 가진 자식 노드를 찾아, 그 노드의 텍스트 값을 /// D3DXVECTOR3 형태로 반환한다. /// \param key 키 /// \return D3DXVECTOR3 해당하는 키를 가진 자식 노드가 있다면 그 노드의 값을 /// 반환하고, 없다면 0.0f 배열을 반환한다. /// \note 해당하는 자식 노드가 없는 경우, 디버그 버전에서는 어서트를 발생시킨다. //////////////////////////////////////////////////////////////////////////////// D3DXVECTOR3 cYamlTree::AttrAsVector3(const std::string& key) const { cYamlTree* child = GetChild(key, true); return child ? child->GetValueAsVector3() : D3DXVECTOR3(0.0f, 0.0f, 0.0f); } //////////////////////////////////////////////////////////////////////////////// /// \brief 해당하는 키를 가진 자식 노드를 찾아, 그 노드의 텍스트 값을 /// D3DXVECTOR4 형태로 반환한다. /// \param key 키 /// \return D3DXVECTOR4 해당하는 키를 가진 자식 노드가 있다면 그 노드의 값을 /// 반환하고, 없다면 0.0f 배열을 반환한다. /// \note 해당하는 자식 노드가 없는 경우, 디버그 버전에서는 어서트를 발생시킨다. //////////////////////////////////////////////////////////////////////////////// D3DXVECTOR4 cYamlTree::AttrAsVector4(const std::string& key) const { cYamlTree* child = GetChild(key, true); return child ? child->GetValueAsVector4() : D3DXVECTOR4(0.0f, 0.0f, 0.0f, 0.0f); } //////////////////////////////////////////////////////////////////////////////// /// \brief 해당하는 키를 가진 자식 노드를 찾아, 그 노드의 텍스트 값을 /// D3DXQUATERNION 형태로 반환한다. /// \param key 키 /// \return D3DXQUATERNION 해당하는 키를 가진 자식 노드가 있다면 그 노드의 값을 /// 반환하고, 없다면 0.0f 배열을 반환한다. /// \note 해당하는 자식 노드가 없는 경우, 디버그 버전에서는 어서트를 발생시킨다. //////////////////////////////////////////////////////////////////////////////// D3DXQUATERNION cYamlTree::AttrAsQuaternion(const std::string& key) const { cYamlTree* child = GetChild(key, true); return child ? child->GetValueAsQuaternion() : D3DXQUATERNION(0.0f, 0.0f, 0.0f, 0.0f); } //////////////////////////////////////////////////////////////////////////////// /// \brief 해당하는 키를 가진 자식 노드를 찾아, 그 노드의 텍스트 값을 /// D3DXCOLOR 형태로 반환한다. /// \param key 키 /// \return D3DXCOLOR 해당하는 키를 가진 자식 노드가 있다면 그 노드의 값을 /// 반환하고, 없다면 0.0f 배열을 반환한다. /// \note 해당하는 자식 노드가 없는 경우, 디버그 버전에서는 어서트를 발생시킨다. //////////////////////////////////////////////////////////////////////////////// D3DXCOLOR cYamlTree::AttrAsColor(const std::string& key) const { cYamlTree* child = GetChild(key, true); return child ? child->GetValueAsColor() : D3DXCOLOR(0.0f, 0.0f, 0.0f, 0.0f); } //////////////////////////////////////////////////////////////////////////////// /// \brief 해당하는 키를 가진 자식 노드를 찾아, 그 노드의 텍스트 값을 /// D3DXVECTOR2 형태로 반환한다. /// \param key 키 /// \param nullValue 해당하는 자식 노드가 존재하지 않을 경우에 반환할 값 /// \return D3DXVECTOR2 해당하는 키를 가진 자식 노드가 있다면 그 노드의 값을 /// 반환하고, 없다면 주어진 nullValue를 반환한다. //////////////////////////////////////////////////////////////////////////////// D3DXVECTOR2 cYamlTree::AttrAsVector2Safe(const std::string& key, const D3DXVECTOR2& nullValue) const { cYamlTree* child = GetChild(key, false); return child ? child->GetValueAsVector2() : nullValue; } //////////////////////////////////////////////////////////////////////////////// /// \brief 해당하는 키를 가진 자식 노드를 찾아, 그 노드의 텍스트 값을 /// D3DXVECTOR3 형태로 반환한다. /// \param key 키 /// \param nullValue 해당하는 자식 노드가 존재하지 않을 경우에 반환할 값 /// \return D3DXVECTOR3 해당하는 키를 가진 자식 노드가 있다면 그 노드의 값을 /// 반환하고, 없다면 주어진 nullValue를 반환한다. //////////////////////////////////////////////////////////////////////////////// D3DXVECTOR3 cYamlTree::AttrAsVector3Safe(const std::string& key, const D3DXVECTOR3& nullValue) const { cYamlTree* child = GetChild(key, false); return child ? child->GetValueAsVector3() : nullValue; } //////////////////////////////////////////////////////////////////////////////// /// \brief 해당하는 키를 가진 자식 노드를 찾아, 그 노드의 텍스트 값을 /// D3DXVECTOR4 형태로 반환한다. /// \param key 키 /// \param nullValue 해당하는 자식 노드가 존재하지 않을 경우에 반환할 값 /// \return D3DXVECTOR4 해당하는 키를 가진 자식 노드가 있다면 그 노드의 값을 /// 반환하고, 없다면 주어진 nullValue를 반환한다. //////////////////////////////////////////////////////////////////////////////// D3DXVECTOR4 cYamlTree::AttrAsVector4Safe(const std::string& key, const D3DXVECTOR4& nullValue) const { cYamlTree* child = GetChild(key, false); return child ? child->GetValueAsVector4() : nullValue; } //////////////////////////////////////////////////////////////////////////////// /// \brief 해당하는 키를 가진 자식 노드를 찾아, 그 노드의 텍스트 값을 /// D3DXQUATERNION 형태로 반환한다. /// \param key 키 /// \param nullValue 해당하는 자식 노드가 존재하지 않을 경우에 반환할 값 /// \return D3DXQUATERNION 해당하는 키를 가진 자식 노드가 있다면 그 노드의 값을 /// 반환하고, 없다면 주어진 nullValue를 반환한다. //////////////////////////////////////////////////////////////////////////////// D3DXQUATERNION cYamlTree::AttrAsQuaternionSafe(const std::string& key, const D3DXQUATERNION& nullValue) const { cYamlTree* child = GetChild(key, false); return child ? child->GetValueAsQuaternion() : nullValue; } //////////////////////////////////////////////////////////////////////////////// /// \brief 해당하는 키를 가진 자식 노드를 찾아, 그 노드의 텍스트 값을 /// D3DXCOLOR 형태로 반환한다. /// \param key 키 /// \param nullValue 해당하는 자식 노드가 존재하지 않을 경우에 반환할 값 /// \return D3DXCOLOR 해당하는 키를 가진 자식 노드가 있다면 그 노드의 값을 /// 반환하고, 없다면 주어진 nullValue를 반환한다. //////////////////////////////////////////////////////////////////////////////// D3DXCOLOR cYamlTree::AttrAsColorSafe(const std::string& key, const D3DXCOLOR& nullValue) const { cYamlTree* child = GetChild(key, false); return child ? child->GetValueAsColor() : nullValue; } //////////////////////////////////////////////////////////////////////////////// /// \brief 자식 노드를 추가한다. /// \param key 키 /// \param value 값 /// \return cYamlTree* 추가한 자식 노드의 포인터 //////////////////////////////////////////////////////////////////////////////// cYamlTree* cYamlTree::AddAttr(const std::string& key, const D3DXVECTOR2& value) { return AddChild(new cYamlTree(key, value)); } //////////////////////////////////////////////////////////////////////////////// /// \brief 자식 노드를 추가한다. /// \param key 키 /// \param value 값 /// \return cYamlTree* 추가한 자식 노드의 포인터 //////////////////////////////////////////////////////////////////////////////// cYamlTree* cYamlTree::AddAttr(const std::string& key, const D3DXVECTOR3& value) { return AddChild(new cYamlTree(key, value)); } //////////////////////////////////////////////////////////////////////////////// /// \brief 자식 노드를 추가한다. /// \param key 키 /// \param value 값 /// \return cYamlTree* 추가한 자식 노드의 포인터 //////////////////////////////////////////////////////////////////////////////// cYamlTree* cYamlTree::AddAttr(const std::string& key, const D3DXVECTOR4& value) { return AddChild(new cYamlTree(key, value)); } //////////////////////////////////////////////////////////////////////////////// /// \brief 자식 노드를 추가한다. /// \param key 키 /// \param value 값 /// \return cYamlTree* 추가한 자식 노드의 포인터 //////////////////////////////////////////////////////////////////////////////// cYamlTree* cYamlTree::AddAttr(const std::string& key, const D3DXQUATERNION& value) { return AddChild(new cYamlTree(key, value)); } //////////////////////////////////////////////////////////////////////////////// /// \brief 자식 노드를 추가한다. /// \param key 키 /// \param value 값 /// \return cYamlTree* 추가한 자식 노드의 포인터 //////////////////////////////////////////////////////////////////////////////// cYamlTree* cYamlTree::AddAttr(const std::string& key, const D3DXCOLOR& value) { return AddChild(new cYamlTree(key, value)); } #endif