DCL 3.7.4
Loading...
Searching...
No Matches
TextTemplate.cpp
Go to the documentation of this file.
1#include <dcl/Config.h>
2
3#ifdef __WINNT__
4 #include <windows.h>
5#endif
6
7#include <wctype.h>
8
9#include <dcl/Object.h>
10
11#if __DCL_HAVE_ALLOC_DEBUG
12#undef __DCL_ALLOC_LEVEL
13#define __DCL_ALLOC_LEVEL __DCL_ALLOC_INTERNAL
14#endif
15
16#include <dcl/Array.h>
17#include <dcl/HashMapT.h>
18#include <dcl/ListT.h>
19#include <dcl/Regex.h>
20#include <dcl/SQL.h>
21#include <dcl/Files.h>
22#include <dcl/Writer.h>
23
24#include <dcl/TextTemplate.h>
25
26#if __DCL_HAVE_THIS_FILE__
27#undef __THIS_FILE__
28static const char_t __THIS_FILE__[] = __T("dcl/TextTemplate.cpp");
29#endif
30
31__DCL_BEGIN_NAMESPACE
32
34{
35 String name; // isEmpty() 이면 일반블록이고, 그 내용은 values에 있다.
36 StringList values; // assigned values
37};
38
41
42#define TEXT_LIST() ((TextList*)__textList)
43#define SUB_TEMPLATE_MAP() ((SubTemplateMap*)__subTemplateMap)
44
46
47TextTemplate::~TextTemplate()
48{
49 delete TEXT_LIST();
50 delete SUB_TEMPLATE_MAP();
51}
52
53TextTemplate::TextTemplate()
54{
55#ifdef __DCL_DEBUG
56 __showEmptyName = false;
57#endif
58 __textList = new TextList;
60}
61
62TextTemplate::TextTemplate(const TextTemplate& _src)
63{
64#ifdef __DCL_DEBUG
65 __showEmptyName = false;
66#endif
67 __textList = new TextList;
69
70 *this = _src;
71}
72
73TextTemplate::TextTemplate(const String& _text)
74{
75#ifdef __DCL_DEBUG
76 __showEmptyName = false;
77#endif
78 __textList = new TextList;
80
81 parse(_text);
82}
83
84TextTemplate& TextTemplate::operator = (const TextTemplate& _src)
85{
86 if (this != &_src) {
87#ifdef __DCL_DEBUG
88 __showEmptyName = _src.__showEmptyName;
89#endif
90 clear();
91
92 *(TEXT_LIST()) = *((TextList*)_src.__textList);
94 SubTemplateMap::ConstIterator it = srcMap.begin();
95 for (; it != srcMap.end(); it++) {
96 (*(SUB_TEMPLATE_MAP()))[(*it).key] = (*it).value;
97 }
98 }
99 return *this;
100}
101
102void TextTemplate::parse(const String& _text)
104{
105 parse(_text.data(), _text.data() + _text.length());
106}
107
108// _name ==> _text
109void TextTemplate::parse(const String& _name, const String& _text)
110{
111 __DCL_ASSERT(!_name.isEmpty());
112
113 // sub 템플릿에 같은 이름을 사용하는 템플릿이 있으면
114 // 기존의 템플릿의 내용을 삭제한다.
115#ifdef __DCL_DEBUG
116 SubTemplateMap::Iterator itMap = SUB_TEMPLATE_MAP()->find(_name);
117 if (itMap != SUB_TEMPLATE_MAP()->end()) {
118 __DCL_TRACE1(L"Warning - exist %ls ==> replaced\n", _name.data());
119 }
120#endif
121 SUB_TEMPLATE_MAP()->erase(_name);
122 (*SUB_TEMPLATE_MAP())[_name].parse(_text);
123}
124
125void TextTemplate::clear()
126{
127 TEXT_LIST()->clear();
128 SubTemplateMap::Iterator itMap = SUB_TEMPLATE_MAP()->begin();
129 for( ; itMap != SUB_TEMPLATE_MAP()->end(); itMap++)
130 (*itMap).value.clear();
131
132 SUB_TEMPLATE_MAP()->clear();
133}
134
135void TextTemplate::reset()
136{
137 TextList::Iterator itList = TEXT_LIST()->begin();
138 for( ; itList != TEXT_LIST()->end(); itList++) {
139 if (!(*itList).name.isEmpty()) {
140 // 매크로 이름이 비어 있지 않은
141 // 즉, 매크로의 values만 삭제한다.
142 // 매크로 이름이 비어 있는것은 일반 블록이다.
143 (*itList).values.clear();
144 }
145 }
146
147 // 서브 템플릿을 reset 한다.
148 SubTemplateMap::Iterator itMap = SUB_TEMPLATE_MAP()->begin();
149 for( ; itMap != SUB_TEMPLATE_MAP()->end(); itMap++)
150 (*itMap).value.reset();
151}
152
153void TextTemplate::erase(const char_t* _name)
154{
155 if (_name != NULL) {
156 TextList::Iterator it = TEXT_LIST()->begin();
157 while(it != TEXT_LIST()->end()) {
158 if ((*it).name == _name)
159 it = TEXT_LIST()->erase(it);
160 else
161 it++;
162 }
163 }
164 else {
165 TextList::Iterator it = TEXT_LIST()->begin();
166 while(it != TEXT_LIST()->end()) {
167 if (!(*it).name.isEmpty())
168 it = TEXT_LIST()->erase(it);
169 else
170 it++;
171 }
172 }
173}
174
175int TextTemplate::append(
176 const char_t* _name,
177 const String& _value, bool _clearExists
178)
179{
180 int nCount = 0;
181 TextList::Iterator it = TEXT_LIST()->begin();
182 if (_name != NULL) {
183 for( ; it != TEXT_LIST()->end(); it++) {
184 if ((*it).name == _name) {
185 if (_clearExists && !(*it).values.isEmpty())
186 (*it).values.clear();
187
188 (*it).values.addTail(_value);
189 nCount++;
190 }
191 }
192 }
193 else {
194 for(; it != TEXT_LIST()->end(); it++) {
195 if (!(*it).name.isEmpty()) {
196 if (_clearExists && !(*it).values.isEmpty())
197 (*it).values.clear();
198
199 (*it).values.addTail(_value);
200 nCount++;
201 }
202 }
203 }
204 return nCount;
205}
206
207int TextTemplate::append(
208 const char_t* _name,
209 const TextTemplate& _template, bool _clearExists
210)
211{
212 __DCL_ASSERT(_name != NULL);
213 __DCL_ASSERT(&_template != this);
214
215 int nCount = 0;
216
217 for(TextList::Iterator it = TEXT_LIST()->begin();
218 it != TEXT_LIST()->end(); it++
219 ) {
220 if ((*it).name == _name) {
221 if (_clearExists && !(*it).values.isEmpty())
222 (*it).values.clear();
223
224 for(TextList::ConstIterator itSrc =
225 ((const TextList*)_template.__textList)->begin();
226 itSrc != ((const TextList*)_template.__textList)->end(); itSrc++
227 ) {
228 if (!(*itSrc).values.isEmpty()) {
229 (*it).values.insert(
230 (*it).values.end(),
231 (*itSrc).values.begin(),
232 (*itSrc).values.end()
233 );
234 }
235#ifdef __DCL_DEBUG
236 else {
237 __DCL_ASSERT(!((*itSrc).name.isEmpty()));
238 if (_template.__showEmptyName) {
239 String name = L'$' + (*itSrc).name;
240 (*it).values.addTail(name);
241 }
242 }
243#endif
244 }
245 nCount++;
246 }
247 }
248 return nCount;
249}
250
251int TextTemplate::append(const StringStringArray& _nameToValues,
252 bool _clearExists)
253{
254 int nCount = 0;
255 for(size_t i = 0; i < _nameToValues.size(); i++)
256 nCount += append(_nameToValues[i].key, _nameToValues[i].value, _clearExists);
257 return nCount;
258}
259
260int TextTemplate::append(_CONST SQLFields& _fields,
261 const String& _fieldIsNullValue, bool _clearExists)
262{
263 int nCount = 0;
264 for(size_t i = 0; i < _fields.count(); i++) {
265 _CONST SQLField& field = _fields[i];
266 nCount += append(
267 field.name(),
268 onSQLFieldValue(field, _fieldIsNullValue),
269 _clearExists
270 );
271 }
272 return nCount;
273}
274
276 const String& _fieldIsNullValue)
277{
278 if (_field.isNull() || _field.dataSize() == 0)
279 return _fieldIsNullValue;
280
281 return _field.asString();
282}
283
284#ifdef __DCL_DEBUG
285void TextTemplate::showEmptyName(bool _show, bool _withSubTemplate)
286{
287 __showEmptyName = _show;
288 if (_withSubTemplate) {
289 SubTemplateMap::Iterator itMap = SUB_TEMPLATE_MAP()->begin();
290 for( ; itMap != SUB_TEMPLATE_MAP()->end(); itMap++)
291 (*itMap).value.showEmptyName(_show, _withSubTemplate);
292 }
293}
294#endif
295
296// 구성된 템플릿의 내용을 출력한다.
297void TextTemplate::printTo(Writer& out) const
299{
300 for(TextList::Iterator itList = TEXT_LIST()->begin();
301 itList != TEXT_LIST()->end(); itList++
302 ) {
303 if (!(*itList).values.isEmpty()) {
304 StringList::Iterator itValues = (*itList).values.begin();
305 for(; itValues != (*itList).values.end(); itValues++)
306 out << *itValues;
307 }
308#ifdef __DCL_DEBUG
309 else {
310 __DCL_ASSERT(!(*itList).name.isEmpty());
311 if (__showEmptyName) {
312 // '$' 가 제거되어 있다 추가하여 출력한다.
313 out << L"$" << (*itList).name;
314 }
315 }
316#endif
317 }
318}
319
320TextTemplate& TextTemplate::operator [] (const String& _name)
321{
322 return (*SUB_TEMPLATE_MAP())[_name];
323}
324
325TextTemplate* TextTemplate::atP(const String& _name) const
326{
327 SubTemplateMap::Iterator itMap = SUB_TEMPLATE_MAP()->find(_name);
328 if (itMap != SUB_TEMPLATE_MAP()->end()) {
329 return &((*itMap).value);
330 }
331 return NULL;
332}
333
334bool TextTemplate::exists(
335 const String& _name,
336 bool bSubTemplate // = true
337) const
338{
339 if (bSubTemplate) {
340 return ((const SubTemplateMap*)__subTemplateMap)->find(_name)
341 != ((const SubTemplateMap*)__subTemplateMap)->end();
342 }
343 else {
344 const TextList* pList = (const TextList*)__textList;
345 TextList::ConstIterator itList = pList->begin();
346 for (; itList != pList->end(); itList++) {
347 if ((*itList).name == _name) {
348 return true;
349 }
350 }
351 }
352 return false;
353}
354
355void TextTemplate::parseHelper(const char_t* _begin, const char_t* _end)
356{
357 try {
358 Regex re(__T("\\$[a-zA-Z0-9_]+"));
359 Regex::MatchResults results;
360 while(_begin < _end && re.search(_begin, _end, results)) {
361 if (results[0].matched && results[0].first < results[0].second) {
362 // $NAME 이전에 텍스트가 있다.
363 if (_begin < results[0].first) {
364 String text(_begin, results[0].first);
365 TextNode node;
366 node.values.addTail(text);
367 TEXT_LIST()->addTail(node);
368 }
369
370 // '$' 는 제외한다.
371 String name(results[0].first + 1, results[0].second);
372 TextNode node;
373 node.name = name;
374 TEXT_LIST()->addTail(node);
375
376 _begin = results[0].second;
377 }
378 }
379
380 if (_begin < _end) {
381 // $NAME 이후에 텍스트가 있다.
382 String text(_begin, _end);
383 TextNode node;
384 node.values.addTail(text);
385 TEXT_LIST()->addTail(node);
386 }
387 }
388 catch(RegexException* e) {
389 __DCL_TRACE1(__T("%ls\n"), e->toString().data());
390 e->destroy();
391 __DCL_ASSERT(false);
392 }
393}
394
395static String __getName(const char_t* _begin, const char_t* _end)
396{
397 const char_t* begin = _begin + 4; // "<!--
398 while(begin < _end && *begin++ != __T('$'))
399 ;
400
401 const char_t* end = begin;
402 while(end < _end && (iswalnum(*end) || *end == __T('_')))
403 end++;
404
405 String r(begin, end);
406 return r;
407}
408
409void TextTemplate::parse(const char_t* _begin, const char_t* _end)
410{
411 try
412 {
413 Regex reBegin(
414 __T("<!--[\t\v\f ]*\\$[a-zA-Z0-9_]+[\t\v\f ]*-->[\t\v\f \r\n]*")
415 );
416
417 Regex::MatchResults matchBegin;
418 while (_begin < _end && reBegin.search(_begin, _end, matchBegin)) {
419 __DCL_ASSERT(matchBegin.size() > 0 && matchBegin[0].matched);
420
421 // sub 템플릿의 시작을 찾았다.
422 String _name = __getName(matchBegin[0].first, matchBegin[0].second);
423 String strEndPattern =
424 __T("<!--[\t\v\f ]*/[\t\v\f ]*\\$")
425 + _name
426 + __T("[\t\v\f ]*-->[\t\v\f \r\n]*");
427
428 Regex reEnd(strEndPattern);
429 Regex::MatchResults matchEnd;
430// __DCL_TRACE1(L"%lc\n", *(psz + matchBegin.rm_eo));
431 if (reEnd.search(matchBegin[0].second, _end, matchEnd)) {
432 // sub 블록 이전에 대하여 템플릿을 구성한다.
433 if (_begin < matchBegin[0].first) {
434 parseHelper(_begin, matchBegin[0].first);
435 }
436
437 // _name 템플릿을 구성한다.
438 // sub 템플릿에 같은 이름을 사용하는 템플릿이 있으면
439 // 기존의 템플릿의 내용을 삭제한다.
440#ifdef __DCL_DEBUG
441 SubTemplateMap::Iterator itMap = SUB_TEMPLATE_MAP()->find(_name);
442 if (itMap != SUB_TEMPLATE_MAP()->end()) {
444 __T("Warning - exists %ls ==> replaced\n"),
445 _name.data()
446 );
447 }
448#endif
449 SUB_TEMPLATE_MAP()->erase(_name);
450
451 TextNode node;
452 node.name = _name;
453 TEXT_LIST()->addTail(node);
454
455 TextTemplate& sub = (*SUB_TEMPLATE_MAP())[_name];
456// __DCL_TRACE2("%ls: %d\n", _name.data(), matchEnd.rm_so);
457 if (matchBegin[0].second < matchEnd[0].first) {
458 sub.parse(matchBegin[0].second, matchEnd[0].first);
459 }
460
461 _begin = matchEnd[0].second;
462// __DCL_TRACE1(L"%ls\n", psz);
463 }
464 else {
465 _begin = matchBegin[0].second;
466 }
467 }
468
469 if (_begin < _end) {
470 // 남은 데이터가 있다
471 parseHelper(_begin, _end);
472 }
473 }
474 catch(RegexException* e) {
475 __DCL_TRACE1(__T("%ls\n"), e->toString().data());
476 e->destroy();
477 __DCL_ASSERT(false);
478 }
479}
480
481__DCL_END_NAMESPACE
#define __THIS_FILE__
Definition _trace.h:14
#define NULL
Definition Config.h:312
wchar_t char_t
Definition Config.h:247
#define _CONST
Definition Config.h:325
#define __DCL_THROWS1(e)
Definition Config.h:152
IOException *size_t r
Definition MediaInfo.cpp:82
#define __DCL_TRACE1(fmt, arg1)
Definition Object.h:399
#define __DCL_ASSERT(expr)
Definition Object.h:394
#define IMPLEMENT_CLASSINFO(class_name, base_class_name)
Definition Object.h:245
#define __T(str)
Definition Object.h:60
HashMap< String, TextTemplate > SubTemplateMap
#define TEXT_LIST()
List< TextNode > TextList
#define SUB_TEMPLATE_MAP()
virtual void destroy()
Definition Exception.cpp:74
ConstIterator begin() const
ConstIterator end() const
ConstIterator end() const
ConstIterator begin() const
size_t size() const
Definition Regex.h:57
Definition Regex.h:32
bool search(const wchar_t *_begin, const wchar_t *_end, unsigned int _flags=0) __DCL_THROWS1(RegexException *)
Definition Regex.cpp:120
Definition SQL.h:48
virtual String onSQLFieldValue(_CONST SQLField &_field, const String &_fieldIsNullValue)
void * __textList
void * __subTemplateMap
void parseHelper(const char_t *_begin, const char_t *_end)
StringList values
String name