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