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