Add parametrized messaging and serialization to exceptions.
[shibboleth/cpp-xmltooling.git] / xmltooling / exceptions.cpp
1 /*\r
2  *  Copyright 2001-2006 Internet2\r
3  * \r
4  * Licensed under the Apache License, Version 2.0 (the "License");\r
5  * you may not use this file except in compliance with the License.\r
6  * You may obtain a copy of the License at\r
7  *\r
8  *     http://www.apache.org/licenses/LICENSE-2.0\r
9  *\r
10  * Unless required by applicable law or agreed to in writing, software\r
11  * distributed under the License is distributed on an "AS IS" BASIS,\r
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
13  * See the License for the specific language governing permissions and\r
14  * limitations under the License.\r
15  */\r
16 \r
17 /**\r
18  * exceptions.cpp\r
19  * \r
20  * Exception classes\r
21  */\r
22  \r
23 #include "internal.h"\r
24 #include "exceptions.h"\r
25 #include "XMLToolingConfig.h"\r
26 #include "util/XMLConstants.h"\r
27 #include "util/XMLHelper.h"\r
28 \r
29 #include <sstream>\r
30 #include <xercesc/util/XMLUniDefs.hpp>\r
31 \r
32 using namespace xmltooling;\r
33 using namespace std;\r
34 \r
35 params::params(int count,...)\r
36 {\r
37     va_list args;\r
38     va_start(args,count);\r
39     while (count--)\r
40         v.push_back(va_arg(args,char*));\r
41     va_end(args);\r
42 }\r
43 \r
44 namedparams::namedparams(int count,...)\r
45 {\r
46     count*=2;\r
47     va_list args;\r
48     va_start(args,count);\r
49     while (count--)\r
50         v.push_back(va_arg(args,char*));\r
51     va_end(args);\r
52 }\r
53 \r
54 XMLToolingException::ExceptionFactoryMap XMLToolingException::m_factoryMap;\r
55 \r
56 XMLToolingException* XMLToolingException::getInstance(const char* exceptionClass)\r
57 {\r
58     if (exceptionClass) {\r
59         ExceptionFactoryMap::const_iterator i=m_factoryMap.find(exceptionClass);\r
60         if (i!=m_factoryMap.end())\r
61             return (i->second)();\r
62     }\r
63     return new XMLToolingException();\r
64 }\r
65 \r
66 XMLToolingException::XMLToolingException(const char* msg, const params& p)\r
67 {\r
68     if (msg)\r
69         m_msg=msg;\r
70     addProperties(p);\r
71 }\r
72 \r
73 XMLToolingException::XMLToolingException(const char* msg, const namedparams& p)\r
74 {\r
75     if (msg)\r
76         m_msg=msg;\r
77     addProperties(p);\r
78 }\r
79 \r
80 XMLToolingException::XMLToolingException(const std::string& msg, const params& p) : m_msg(msg)\r
81 {\r
82     addProperties(p);\r
83 }\r
84 \r
85 XMLToolingException::XMLToolingException(const std::string& msg, const namedparams& p) : m_msg(msg)\r
86 {\r
87     addProperties(p);\r
88 }\r
89 \r
90 void XMLToolingException::setMessage(const char* msg)\r
91 {\r
92     if (msg)\r
93         m_msg=msg;\r
94     else\r
95         m_msg.erase();\r
96     m_processedmsg.erase();\r
97 }\r
98 \r
99 inline const char* get_digit_character()\r
100 {\r
101     static const char  s_characters[19] = \r
102     {\r
103             '9'\r
104         ,   '8'\r
105         ,   '7'\r
106         ,   '6'\r
107         ,   '5'\r
108         ,   '4'\r
109         ,   '3'\r
110         ,   '2'\r
111         ,   '1'\r
112         ,   '0'\r
113         ,   '1'\r
114         ,   '2'\r
115         ,   '3'\r
116         ,   '4'\r
117         ,   '5'\r
118         ,   '6'\r
119         ,   '7'\r
120         ,   '8'\r
121         ,   '9'\r
122     };\r
123     static const char  *s_mid  =   s_characters + 9;\r
124 \r
125     return s_mid;\r
126 }\r
127 \r
128 inline const char* unsigned_integer_to_string(char* buf, size_t cchBuf, int i)\r
129 {\r
130     char* psz=buf + cchBuf - 1;     // Set psz to last char\r
131     *psz = 0;                       // Set terminating null\r
132 \r
133     do {\r
134         unsigned int lsd = i % 10;  // Get least significant\r
135                                     // digit\r
136 \r
137         i /= 10;                    // Prepare for next most\r
138                                     // significant digit\r
139 \r
140         --psz;                      // Move back\r
141 \r
142         *psz = get_digit_character()[lsd]; // Place the digit\r
143 \r
144     } while(i!=0 && psz>buf);\r
145 \r
146     return psz;\r
147 }\r
148 \r
149 void XMLToolingException::addProperties(const params& p)\r
150 {\r
151     m_processedmsg.erase();\r
152     int i=m_params.size()+1;\r
153     char buf[20];\r
154     const vector<const char*>& v=p.get();\r
155     for (vector<const char*>::const_iterator ci=v.begin(); ci!=v.end(); ci++) {\r
156         m_params[unsigned_integer_to_string(buf,sizeof(buf),i++)] = *ci;\r
157     }\r
158 }\r
159         \r
160 void XMLToolingException::addProperties(const namedparams& p)\r
161 {\r
162     m_processedmsg.erase();\r
163     const vector<const char*>& v=p.get();\r
164     for (vector<const char*>::const_iterator ci=v.begin(); ci!=v.end(); ci++) {\r
165         m_params.erase(*ci);\r
166         m_params[*ci] = *(ci+1);\r
167         ci++;   // advance past name to value, then loop will advance it again\r
168     }\r
169 }\r
170 \r
171 const char* XMLToolingException::getProperty(unsigned int index) const\r
172 {\r
173     char buf[20];\r
174     map<string,string>::const_iterator i=m_params.find(unsigned_integer_to_string(buf,sizeof(buf),index));\r
175     return (i==m_params.end()) ? NULL : i->second.c_str();\r
176 }\r
177 \r
178 const char* XMLToolingException::getProperty(const char* name) const\r
179 {\r
180     map<string,string>::const_iterator i=m_params.find(name);\r
181     return (i==m_params.end()) ? NULL : i->second.c_str();\r
182 }\r
183 \r
184 const char* XMLToolingException::getMessage() const\r
185 {\r
186     if (!m_processedmsg.empty())\r
187         return m_processedmsg.c_str();\r
188     else if (m_params.empty())\r
189         return m_msg.c_str();\r
190 \r
191     static const char* legal="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890_";\r
192 \r
193     // Replace any parameters in the message.\r
194     string::size_type i=0,start=0;\r
195     while (start!=string::npos && start<m_msg.length() && (i=m_msg.find("$",start))!=string::npos) {\r
196         if (i>start)\r
197             m_processedmsg += m_msg.substr(start,i-start);  // append everything in between\r
198         start=i+1;                                  // move start to the beginning of the token name\r
199         i=m_msg.find_first_not_of(legal,start);     // find token delimiter\r
200         if (i==start) {                             // append a non legal character\r
201            m_processedmsg+=m_msg[start++];\r
202            continue;\r
203         }\r
204         \r
205         // search for token in map\r
206         map<string,string>::const_iterator param=m_params.find(m_msg.substr(start,(i==string::npos) ? i : i-start));\r
207         if (param!=m_params.end()) {\r
208             m_processedmsg+=param->second;\r
209             start=i;\r
210         }\r
211     }\r
212     if (start!=string::npos && start<m_msg.length())\r
213         m_processedmsg += m_msg.substr(start,i);    // append rest of string\r
214     return m_processedmsg.c_str();\r
215 }\r
216 \r
217 string XMLToolingException::toString() const\r
218 {\r
219     string xml=string("<exception xmlns=\"http://www.opensaml.org/xmltooling\" type=\"") + getClassName() + "\">";\r
220     const char* msg=getMessage();\r
221     if (msg)\r
222         xml=xml + "<message>" + msg + "</message>";\r
223     for (map<string,string>::const_iterator i=m_params.begin(); i!=m_params.end(); i++) {\r
224         xml=xml + "<param name=\"" + i->first + "\">" + i->second + "</param>";\r
225     }\r
226     xml+="</exception>";\r
227     return xml;\r
228 }\r
229 \r
230 XMLToolingException* XMLToolingException::fromStream(std::istream& in)\r
231 {\r
232     static const XMLCh exception[] = { chLatin_e, chLatin_x, chLatin_c, chLatin_e, chLatin_p, chLatin_t, chLatin_i, chLatin_o, chLatin_n, chNull };\r
233     static const XMLCh message[] = { chLatin_m, chLatin_e, chLatin_s, chLatin_s, chLatin_a, chLatin_g, chLatin_e, chNull };\r
234     static const XMLCh name[] = { chLatin_n, chLatin_a, chLatin_m, chLatin_e, chNull };\r
235     static const XMLCh param[] = { chLatin_p, chLatin_a, chLatin_r, chLatin_a, chLatin_m, chNull };\r
236     static const XMLCh type[] = { chLatin_t, chLatin_y, chLatin_p, chLatin_e, chNull };\r
237 \r
238     DOMDocument* doc=XMLToolingInternalConfig::getInternalConfig().m_parserPool->parse(in);\r
239     \r
240     // Check root element.\r
241     const DOMElement* root=doc->getDocumentElement();\r
242     if (!XMLHelper::isNodeNamed(root,XMLConstants::XMLTOOLING_NS,exception)) {\r
243         doc->release();\r
244         throw XMLToolingException("Invalid root element on serialized exception.");\r
245     }\r
246     \r
247     auto_ptr_char classname(root->getAttributeNS(NULL,type));\r
248     auto_ptr<XMLToolingException> excep(XMLToolingException::getInstance(classname.get()));\r
249     \r
250     DOMElement* child=XMLHelper::getFirstChildElement(root,XMLConstants::XMLTOOLING_NS,message);\r
251     if (child && child->hasChildNodes()) {\r
252         auto_ptr_char m(child->getFirstChild()->getNodeValue());\r
253         excep->setMessage(m.get());\r
254     }\r
255     \r
256     child=XMLHelper::getFirstChildElement(root,XMLConstants::XMLTOOLING_NS,param);\r
257     while (child && child->hasChildNodes()) {\r
258         auto_ptr_char n(child->getAttributeNS(NULL,name));\r
259         char* v=toUTF8(child->getFirstChild()->getNodeValue());\r
260         if (n.get() && v)\r
261             excep->addProperty(n.get(), v);\r
262         XMLString::release(&v);\r
263         child=XMLHelper::getNextSiblingElement(root,XMLConstants::XMLTOOLING_NS,param);\r
264     }\r
265 \r
266     doc->release();\r
267     return excep.release();\r
268 }\r
269         \r
270 XMLToolingException* XMLToolingException::fromString(const char* s)\r
271 {\r
272     istringstream in(s);\r
273     return fromStream(in);\r
274 }\r