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