Remove dynamic exceptions
[anna.git] / source / core / util / TextComposer.cpp
1 // ANNA - Anna is Not Nothingness Anymore                                                         //
2 //                                                                                                //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo                         //
4 //                                                                                                //
5 // See project site at http://redmine.teslayout.com/projects/anna-suite                           //
6 // See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
7
8
9 #include <stdio.h>
10
11 #include <anna/core/functions.hpp>
12 #include <anna/core/mt/Guard.hpp>
13 #include <anna/core/tracing/Logger.hpp>
14 #include <anna/core/tracing/TraceMethod.hpp>
15 #include <anna/core/util/Tokenizer.hpp>
16 #include <anna/core/util/defines.hpp>
17
18 #include <anna/core/util/TextComposer.hpp>
19 #include <anna/core/util/TextVariable.hpp>
20
21 using namespace std;
22 using namespace anna;
23
24 //---------------------------------------------------------------------------------------
25 // (1) Cada uno de los DynamicText se encarga de liberar la memoria de sus componentes.
26 //---------------------------------------------------------------------------------------
27 TextComposer::~TextComposer() {
28   for(variable_iterator ii = begin(), maxii = end(); ii != maxii; ii ++)
29     delete textVariable(ii);
30
31   container::clear();
32   delete a_prefix;
33 }
34
35 /*
36  * Este método se invoca desde el TextManager::create.
37  * Divide la expressión en partes más fáciles de analizar.
38  *
39  * La expresión puede tener la forma "texto inicial ${variable1:%s} siguiente texto ${variable2:%02d} el resto"
40  *
41  * Y quedará como:
42  *    a_prefix = "texto inicial "
43  *    var1 => nombre = variable1 => "%s siguiente texto"
44  *    var2 => nombre = variable2 => "%02d el resto"
45  */
46 void TextComposer::initialize()
47 noexcept(false) {
48   Tokenizer variables(a_expression, "${");
49   Tokenizer idAndText;
50   Tokenizer nameAndFormat;
51   String expression;
52   Variable::Type::_v type;
53   TextVariable* variable;
54
55   // Si el texto no tiene ninguna variable
56   if(variables.size() == 1) {
57     a_prefix = new String(a_expression);
58     return;
59   }
60
61   try {
62     bool init = true;
63
64     for(Tokenizer::const_iterator ii = variables.begin(), maxii = variables.end(); ii != maxii; ii ++) {
65       if(anna_strchr(Tokenizer::data(ii), '}') == NULL) {
66         if(init == true) {
67           a_prefix = new String(Tokenizer::data(ii));
68           init = false;
69           continue;
70         } else
71           throw RuntimeException("Parenthesis are not balanced", ANNA_FILE_LOCATION);
72       }
73
74       // El id [0] contendrá el <name>:<format>
75       // El id [1] contendrá el texto (si es que hay).
76       idAndText.apply(Tokenizer::data(ii), "}");
77       nameAndFormat.apply(idAndText [0], ":");
78
79       if(find(nameAndFormat [0], Exception::Mode::Ignore) != NULL) {
80         String msg("Variable ");
81         msg << nameAndFormat [0] << " already defined";
82         throw RuntimeException(msg, ANNA_FILE_LOCATION);
83       }
84
85       type = calculeType(nameAndFormat [1]);
86       expression = nameAndFormat [1];
87
88       // Es posible que no haya texto que acompañe a identificador
89       if(idAndText.size() > 1)
90         expression += idAndText [1];
91
92       push_back(variable = new TextVariable(nameAndFormat [0], type, expression));
93       LOGDEBUG(
94         String msg("TextComposer::initialize | New Variable: ");
95         msg << variable->asString();
96         Logger::debug(msg, ANNA_FILE_LOCATION)
97       );
98       init = false;
99     }
100   } catch(RuntimeException& ex) {
101     String msg(asString());
102     msg << " | " << ex.getText();
103     throw RuntimeException(msg, ex.getFromFile(), ex.getFromLine());
104   }
105 }
106
107 TextVariable* TextComposer::find(const char* name, const Exception::Mode::_v emode)
108 noexcept(false) {
109   for(variable_iterator ii = begin(), maxii = end(); ii != maxii; ii ++) {
110     if(anna_strcmp(textVariable(ii)->getName(), name) == 0)
111       return textVariable(ii);
112   }
113
114   if(emode == Exception::Mode::Ignore)
115     return NULL;
116
117   String msg(asString());
118   msg << " | TextVariable: " << name << " | It was not found";
119
120   if(emode == Exception::Mode::Throw)
121     throw RuntimeException(msg, ANNA_FILE_LOCATION);
122
123   Logger::error(msg, ANNA_FILE_LOCATION);
124   return NULL;
125 }
126
127 String TextComposer::apply() const
128 noexcept(false) {
129   LOGMETHOD(TraceMethod ttmm(Logger::Local7, "TextComposer::apply", ANNA_FILE_LOCATION));
130   String result;
131 // No hace falta proteger porque sólo se podrá acceder desde el Protector
132 //   Guard <TextComposer> guard (this);
133
134   if(a_prefix != NULL)
135     result = *a_prefix;
136
137   try {
138     a_buffer.clear();
139
140     for(const_variable_iterator ii = begin(), maxii = end(); ii != maxii; ii ++)
141       result += textVariable(ii)->compose(a_buffer);
142
143     LOGDEBUG(
144       String msg("TextComposer { Id: ");
145       msg << a_id << " | Text: " << result << " }";
146       Logger::debug(msg, ANNA_FILE_LOCATION);
147     );
148   } catch(RuntimeException& ex) {
149     String msg(asString());
150     msg << " | " << ex.getText();
151     throw RuntimeException(msg, ANNA_FILE_LOCATION);
152   }
153
154   return result;
155 }
156
157 String TextComposer::asString() const
158 {
159   String result("TextComposer { Id: ");
160   result << a_id;
161   result << " | Expression: " << a_expression;
162   return result << " }";
163 }
164
165 //static
166 TextVariable::Type::_v TextComposer::calculeType(const char* format)
167 noexcept(false) {
168   static const char* typeInteger = "[:digit:]*d";
169   static const char* typeString = "[:digit:]*s";
170   static const char* typeFloat = "([:digit:]*.[:digit:]|.[:digit:]*)f|f";
171   static const char* typeInteger64 = "[:digit:]*(ld|lld)";
172
173   if(*format != '%')
174     throw RuntimeException("Format must begin with character %%", ANNA_FILE_LOCATION);
175
176   if(functions::isLike(typeInteger, format))
177     return Variable::Type::Integer;
178
179   if(functions::isLike(typeString, format))
180     return Variable::Type::String;
181
182   if(functions::isLike(typeFloat, format))
183     return Variable::Type::Float;
184
185   if(functions::isLike(typeInteger64, format))
186     return Variable::Type::Integer64;
187
188   String msg("Format '");
189   msg << format << "' is not recognized";
190   throw RuntimeException(msg, ANNA_FILE_LOCATION);
191 }
192