1 // ANNA - Anna is Not 'N' Anymore
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
5 // https://bitbucket.org/testillano/anna
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
11 // * Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 // * Redistributions in binary form must reproduce the above
14 // copyright notice, this list of conditions and the following disclaimer
15 // in the documentation and/or other materials provided with the
17 // * Neither the name of Google Inc. nor the names of its
18 // contributors may be used to endorse or promote products derived from
19 // this software without specific prior written permission.
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 // Authors: eduardo.ramos.testillano@gmail.com
34 // cisco.tierra@gmail.com
39 #include <anna/core/functions.hpp>
40 #include <anna/core/mt/Guard.hpp>
41 #include <anna/core/tracing/Logger.hpp>
42 #include <anna/core/tracing/TraceMethod.hpp>
43 #include <anna/core/util/Tokenizer.hpp>
44 #include <anna/core/util/defines.hpp>
46 #include <anna/core/util/TextComposer.hpp>
47 #include <anna/core/util/TextVariable.hpp>
52 //---------------------------------------------------------------------------------------
53 // (1) Cada uno de los DynamicText se encarga de liberar la memoria de sus componentes.
54 //---------------------------------------------------------------------------------------
55 TextComposer::~TextComposer() {
56 for(variable_iterator ii = begin(), maxii = end(); ii != maxii; ii ++)
57 delete textVariable(ii);
64 * Este método se invoca desde el TextManager::create.
65 * Divide la expressión en partes más fáciles de analizar.
67 * La expresión puede tener la forma "texto inicial ${variable1:%s} siguiente texto ${variable2:%02d} el resto"
70 * a_prefix = "texto inicial "
71 * var1 => nombre = variable1 => "%s siguiente texto"
72 * var2 => nombre = variable2 => "%02d el resto"
74 void TextComposer::initialize()
75 throw(RuntimeException) {
76 Tokenizer variables(a_expression, "${");
78 Tokenizer nameAndFormat;
80 Variable::Type::_v type;
81 TextVariable* variable;
83 // Si el texto no tiene ninguna variable
84 if(variables.size() == 1) {
85 a_prefix = new String(a_expression);
92 for(Tokenizer::const_iterator ii = variables.begin(), maxii = variables.end(); ii != maxii; ii ++) {
93 if(anna_strchr(Tokenizer::data(ii), '}') == NULL) {
95 a_prefix = new String(Tokenizer::data(ii));
99 throw RuntimeException("Parenthesis are not balanced", ANNA_FILE_LOCATION);
102 // El id [0] contendrá el <name>:<format>
103 // El id [1] contendrá el texto (si es que hay).
104 idAndText.apply(Tokenizer::data(ii), "}");
105 nameAndFormat.apply(idAndText [0], ":");
107 if(find(nameAndFormat [0], Exception::Mode::Ignore) != NULL) {
108 String msg("Variable ");
109 msg << nameAndFormat [0] << " already defined";
110 throw RuntimeException(msg, ANNA_FILE_LOCATION);
113 type = calculeType(nameAndFormat [1]);
114 expression = nameAndFormat [1];
116 // Es posible que no haya texto que acompañe a identificador
117 if(idAndText.size() > 1)
118 expression += idAndText [1];
120 push_back(variable = new TextVariable(nameAndFormat [0], type, expression));
122 String msg("TextComposer::initialize | New Variable: ");
123 msg << variable->asString();
124 Logger::debug(msg, ANNA_FILE_LOCATION)
128 } catch(RuntimeException& ex) {
129 String msg(asString());
130 msg << " | " << ex.getText();
131 throw RuntimeException(msg, ex.getFromFile(), ex.getFromLine());
135 TextVariable* TextComposer::find(const char* name, const Exception::Mode::_v emode)
136 throw(RuntimeException) {
137 for(variable_iterator ii = begin(), maxii = end(); ii != maxii; ii ++) {
138 if(anna_strcmp(textVariable(ii)->getName(), name) == 0)
139 return textVariable(ii);
142 if(emode == Exception::Mode::Ignore)
145 String msg(asString());
146 msg << " | TextVariable: " << name << " | It was not found";
148 if(emode == Exception::Mode::Throw)
149 throw RuntimeException(msg, ANNA_FILE_LOCATION);
151 Logger::error(msg, ANNA_FILE_LOCATION);
155 String TextComposer::apply() const
156 throw(RuntimeException) {
157 LOGMETHOD(TraceMethod ttmm(Logger::Local7, "TextComposer::apply", ANNA_FILE_LOCATION));
159 // No hace falta proteger porque sólo se podrá acceder desde el Protector
160 // Guard <TextComposer> guard (this);
168 for(const_variable_iterator ii = begin(), maxii = end(); ii != maxii; ii ++)
169 result += textVariable(ii)->compose(a_buffer);
172 String msg("TextComposer { Id: ");
173 msg << a_id << " | Text: " << result << " }";
174 Logger::debug(msg, ANNA_FILE_LOCATION);
176 } catch(RuntimeException& ex) {
177 String msg(asString());
178 msg << " | " << ex.getText();
179 throw RuntimeException(msg, ANNA_FILE_LOCATION);
185 String TextComposer::asString() const
187 String result("TextComposer { Id: ");
189 result << " | Expression: " << a_expression;
190 return result << " }";
194 TextVariable::Type::_v TextComposer::calculeType(const char* format)
195 throw(RuntimeException) {
196 static const char* typeInteger = "[:digit:]*d";
197 static const char* typeString = "[:digit:]*s";
198 static const char* typeFloat = "([:digit:]*.[:digit:]|.[:digit:]*)f|f";
199 static const char* typeInteger64 = "[:digit:]*(ld|lld)";
202 throw RuntimeException("Format must begin with character %%", ANNA_FILE_LOCATION);
204 if(functions::isLike(typeInteger, format))
205 return Variable::Type::Integer;
207 if(functions::isLike(typeString, format))
208 return Variable::Type::String;
210 if(functions::isLike(typeFloat, format))
211 return Variable::Type::Float;
213 if(functions::isLike(typeInteger64, format))
214 return Variable::Type::Integer64;
216 String msg("Format '");
217 msg << format << "' is not recognized";
218 throw RuntimeException(msg, ANNA_FILE_LOCATION);