1//
2// Document.cpp
3//
4// Library: MongoDB
5// Package: MongoDB
6// Module: Document
7//
8// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
9// and Contributors.
10//
11// SPDX-License-Identifier: BSL-1.0
12//
13
14
15#include "Poco/MongoDB/Document.h"
16#include "Poco/MongoDB/Binary.h"
17#include "Poco/MongoDB/ObjectId.h"
18#include "Poco/MongoDB/Array.h"
19#include "Poco/MongoDB/RegularExpression.h"
20#include "Poco/MongoDB/JavaScriptCode.h"
21#include <sstream>
22
23
24namespace Poco {
25namespace MongoDB {
26
27
28Document::Document()
29{
30}
31
32
33Document::~Document()
34{
35}
36
37
38Element::Ptr Document::get(const std::string& name) const
39{
40 Element::Ptr element;
41
42 ElementSet::const_iterator it = std::find_if(_elements.begin(), _elements.end(), ElementFindByName(name));
43 if (it != _elements.end())
44 {
45 return *it;
46 }
47
48 return element;
49}
50
51
52Int64 Document::getInteger(const std::string& name) const
53{
54 Element::Ptr element = get(name);
55 if (element.isNull()) throw Poco::NotFoundException(name);
56
57 if (ElementTraits<double>::TypeId == element->type())
58 {
59 ConcreteElement<double>* concrete = dynamic_cast<ConcreteElement<double>*>(element.get());
60 if (concrete) return static_cast<Int64>(concrete->value());
61 }
62 else if (ElementTraits<Int32>::TypeId == element->type())
63 {
64 ConcreteElement<Int32>* concrete = dynamic_cast<ConcreteElement<Int32>*>(element.get());
65 if (concrete) return concrete->value();
66 }
67 else if (ElementTraits<Int64>::TypeId == element->type())
68 {
69 ConcreteElement<Int64>* concrete = dynamic_cast<ConcreteElement<Int64>*>(element.get());
70 if (concrete) return concrete->value();
71 }
72 throw Poco::BadCastException("Invalid type mismatch!");
73}
74
75
76void Document::read(BinaryReader& reader)
77{
78 int size;
79 reader >> size;
80
81 unsigned char type;
82 reader >> type;
83
84 while (type != '\0')
85 {
86 Element::Ptr element;
87
88 std::string name = BSONReader(reader).readCString();
89
90 switch (type)
91 {
92 case ElementTraits<double>::TypeId:
93 element = new ConcreteElement<double>(name, 0);
94 break;
95 case ElementTraits<Int32>::TypeId:
96 element = new ConcreteElement<Int32>(name, 0);
97 break;
98 case ElementTraits<std::string>::TypeId:
99 element = new ConcreteElement<std::string>(name, "");
100 break;
101 case ElementTraits<Document::Ptr>::TypeId:
102 element = new ConcreteElement<Document::Ptr>(name, new Document);
103 break;
104 case ElementTraits<Array::Ptr>::TypeId:
105 element = new ConcreteElement<Array::Ptr>(name, new Array);
106 break;
107 case ElementTraits<Binary::Ptr>::TypeId:
108 element = new ConcreteElement<Binary::Ptr>(name, new Binary);
109 break;
110 case ElementTraits<ObjectId::Ptr>::TypeId:
111 element = new ConcreteElement<ObjectId::Ptr>(name, new ObjectId);
112 break;
113 case ElementTraits<bool>::TypeId:
114 element = new ConcreteElement<bool>(name, false);
115 break;
116 case ElementTraits<Poco::Timestamp>::TypeId:
117 element = new ConcreteElement<Poco::Timestamp>(name, Poco::Timestamp());
118 break;
119 case ElementTraits<BSONTimestamp>::TypeId:
120 element = new ConcreteElement<BSONTimestamp>(name, BSONTimestamp());
121 break;
122 case ElementTraits<NullValue>::TypeId:
123 element = new ConcreteElement<NullValue>(name, NullValue(0));
124 break;
125 case ElementTraits<RegularExpression::Ptr>::TypeId:
126 element = new ConcreteElement<RegularExpression::Ptr>(name, new RegularExpression());
127 break;
128 case ElementTraits<JavaScriptCode::Ptr>::TypeId:
129 element = new ConcreteElement<JavaScriptCode::Ptr>(name, new JavaScriptCode());
130 break;
131 case ElementTraits<Int64>::TypeId:
132 element = new ConcreteElement<Int64>(name, 0);
133 break;
134 default:
135 {
136 std::stringstream ss;
137 ss << "Element " << name << " contains an unsupported type 0x" << std::hex << (int) type;
138 throw Poco::NotImplementedException(ss.str());
139 }
140 //TODO: x0F -> JavaScript code with scope
141 // xFF -> Min Key
142 // x7F -> Max Key
143 }
144
145 element->read(reader);
146 _elements.push_back(element);
147
148 reader >> type;
149 }
150}
151
152
153std::string Document::toString(int indent) const
154{
155 std::ostringstream oss;
156
157 oss << '{';
158
159 if (indent > 0) oss << std::endl;
160
161
162 for (ElementSet::const_iterator it = _elements.begin(); it != _elements.end(); ++it)
163 {
164 if (it != _elements.begin())
165 {
166 oss << ',';
167 if (indent > 0) oss << std::endl;
168 }
169
170 for (int i = 0; i < indent; ++i) oss << ' ';
171
172 oss << '"' << (*it)->name() << '"';
173 oss << (indent > 0 ? " : " : ":");
174
175 oss << (*it)->toString(indent > 0 ? indent + 2 : 0);
176 }
177
178 if (indent > 0)
179 {
180 oss << std::endl;
181 if (indent >= 2) indent -= 2;
182
183 for (int i = 0; i < indent; ++i) oss << ' ';
184 }
185
186 oss << '}';
187
188 return oss.str();
189}
190
191
192void Document::write(BinaryWriter& writer)
193{
194 if (_elements.empty())
195 {
196 writer << 5;
197 }
198 else
199 {
200 std::stringstream sstream;
201 Poco::BinaryWriter tempWriter(sstream);
202 for (ElementSet::iterator it = _elements.begin(); it != _elements.end(); ++it)
203 {
204 tempWriter << static_cast<unsigned char>((*it)->type());
205 BSONWriter(tempWriter).writeCString((*it)->name());
206 Element::Ptr element = *it;
207 element->write(tempWriter);
208 }
209 tempWriter.flush();
210
211 Poco::Int32 len = static_cast<Poco::Int32>(5 + sstream.tellp()); /* 5 = sizeof(len) + 0-byte */
212 writer << len;
213 writer.writeRaw(sstream.str());
214 }
215 writer << '\0';
216}
217
218
219} } // namespace Poco::MongoDB
220