https://issues.shibboleth.net/jira/browse/CPPXT-46
[shibboleth/cpp-xmltooling.git] / xmltooling / AbstractXMLObject.cpp
1 /*
2 *  Copyright 2001-2009 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  * AbstractXMLObject.cpp
19  *
20  * An abstract implementation of XMLObject.
21  */
22
23 #include "internal.h"
24 #include "exceptions.h"
25 #include "AbstractXMLObject.h"
26 #include "util/DateTime.h"
27
28 #include <algorithm>
29
30 using namespace xmltooling;
31 using std::set;
32
33 using xercesc::XMLString;
34
35 XMLObject::XMLObject()
36 {
37 }
38
39 XMLObject::~XMLObject()
40 {
41 }
42
43 void XMLObject::releaseThisandParentDOM() const
44 {
45     releaseDOM();
46     releaseParentDOM(true);
47 }
48
49 void XMLObject::releaseThisAndChildrenDOM() const
50 {
51     releaseChildrenDOM(true);
52     releaseDOM();
53 }
54
55 AbstractXMLObject::AbstractXMLObject(const XMLCh* nsURI, const XMLCh* localName, const XMLCh* prefix, const QName* schemaType)
56     : m_log(logging::Category::getInstance(XMLTOOLING_LOGCAT".XMLObject")),
57         m_schemaLocation(NULL), m_noNamespaceSchemaLocation(NULL), m_nil(xmlconstants::XML_BOOL_NULL),
58         m_parent(NULL), m_elementQname(nsURI, localName, prefix), m_typeQname(NULL)
59 {
60     addNamespace(Namespace(nsURI, prefix));
61     if (schemaType) {
62         m_typeQname = new QName(*schemaType);
63         addNamespace(Namespace(m_typeQname->getNamespaceURI(), m_typeQname->getPrefix()));
64     }
65 }
66
67 AbstractXMLObject::AbstractXMLObject(const AbstractXMLObject& src)
68     : m_namespaces(src.m_namespaces), m_log(src.m_log), m_schemaLocation(XMLString::replicate(src.m_schemaLocation)),
69         m_noNamespaceSchemaLocation(XMLString::replicate(src.m_noNamespaceSchemaLocation)), m_nil(src.m_nil),
70         m_parent(NULL), m_elementQname(src.m_elementQname), m_typeQname(NULL)
71 {
72     if (src.m_typeQname)
73         m_typeQname=new QName(*src.m_typeQname);
74 }
75
76 AbstractXMLObject::~AbstractXMLObject()
77 {
78     delete m_typeQname;
79     xercesc::XMLString::release(&m_schemaLocation);
80     xercesc::XMLString::release(&m_noNamespaceSchemaLocation);
81 }
82
83 void AbstractXMLObject::detach()
84 {
85     if (!getParent())
86         return;
87     else if (getParent()->hasParent())
88         throw XMLObjectException("Cannot detach an object whose parent is itself a child.");
89
90     // Pull ourselves out of the parent and then blast him.
91     getParent()->removeChild(this);
92     delete m_parent;
93     m_parent = NULL;
94 }
95
96 const QName& AbstractXMLObject::getElementQName() const
97 {
98     return m_elementQname;
99 }
100
101 const set<Namespace>& AbstractXMLObject::getNamespaces() const
102 {
103     return m_namespaces;
104 }
105
106 void XMLObject::setNil(const XMLCh* value)
107 {
108     if (value) {
109         switch (*value) {
110             case xercesc::chLatin_t:
111                 nil(xmlconstants::XML_BOOL_TRUE);
112                 break;
113             case xercesc::chLatin_f:
114                 nil(xmlconstants::XML_BOOL_FALSE);
115                 break;
116             case xercesc::chDigit_1:
117                 nil(xmlconstants::XML_BOOL_ONE);
118                 break;
119             case xercesc::chDigit_0:
120                 nil(xmlconstants::XML_BOOL_ZERO);
121                 break;
122             default:
123                 nil(xmlconstants::XML_BOOL_NULL);
124         }
125     }
126     else {
127         nil(xmlconstants::XML_BOOL_NULL);
128     }
129 }
130
131 void AbstractXMLObject::addNamespace(const Namespace& ns) const
132 {
133     std::set<Namespace>::iterator i = m_namespaces.find(ns);
134     if (i == m_namespaces.end())
135         m_namespaces.insert(ns);
136     else if (ns.alwaysDeclare())
137         const_cast<Namespace&>(*i).setAlwaysDeclare(true);
138 }
139
140 void AbstractXMLObject::removeNamespace(const Namespace& ns)
141 {
142     m_namespaces.erase(ns);
143 }
144
145 const QName* AbstractXMLObject::getSchemaType() const
146 {
147     return m_typeQname;
148 }
149
150 const XMLCh* AbstractXMLObject::getXMLID() const
151 {
152     return NULL;
153 }
154
155 xmlconstants::xmltooling_bool_t AbstractXMLObject::getNil() const
156 {
157     return m_nil;
158 }
159
160 void AbstractXMLObject::nil(xmlconstants::xmltooling_bool_t value)
161 {
162     if (m_nil != value) {
163         releaseThisandParentDOM();
164         m_nil = value;
165     }
166 }
167
168 bool AbstractXMLObject::hasParent() const
169 {
170     return m_parent != NULL;
171 }
172
173 XMLObject* AbstractXMLObject::getParent() const
174 {
175     return m_parent;
176 }
177
178 void AbstractXMLObject::setParent(XMLObject* parent)
179 {
180     m_parent = parent;
181 }
182
183 XMLCh* AbstractXMLObject::prepareForAssignment(XMLCh* oldValue, const XMLCh* newValue)
184 {
185     if (!XMLString::equals(oldValue,newValue)) {
186         releaseThisandParentDOM();
187         XMLCh* newString = XMLString::replicate(newValue);
188         XMLString::release(&oldValue);
189         return newString;
190     }
191     return oldValue;
192 }
193
194 QName* AbstractXMLObject::prepareForAssignment(QName* oldValue, const QName* newValue)
195 {
196     if (!oldValue) {
197         if (newValue) {
198             releaseThisandParentDOM();
199             Namespace newNamespace(newValue->getNamespaceURI(), newValue->getPrefix());
200             addNamespace(newNamespace);
201             return new QName(*newValue);
202         }
203         return NULL;
204     }
205
206     delete oldValue;
207     releaseThisandParentDOM();
208     if (newValue) {
209         Namespace newNamespace(newValue->getNamespaceURI(), newValue->getPrefix());
210         addNamespace(newNamespace);
211         return new QName(*newValue);
212     }
213     return NULL;
214 }
215
216 DateTime* AbstractXMLObject::prepareForAssignment(DateTime* oldValue, const DateTime* newValue)
217 {
218     if (!oldValue) {
219         if (newValue) {
220             releaseThisandParentDOM();
221             return new DateTime(*newValue);
222         }
223         return NULL;
224     }
225
226     delete oldValue;
227     releaseThisandParentDOM();
228     return newValue ? new DateTime(*newValue) : NULL;
229 }
230
231 DateTime* AbstractXMLObject::prepareForAssignment(DateTime* oldValue, time_t newValue, bool duration)
232 {
233     delete oldValue;
234     releaseThisandParentDOM();
235     DateTime* ret = new DateTime(newValue, duration);
236     if (duration)
237         ret->parseDuration();
238     else
239         ret->parseDateTime();
240     return ret;
241 }
242
243 DateTime* AbstractXMLObject::prepareForAssignment(DateTime* oldValue, const XMLCh* newValue, bool duration)
244 {
245     delete oldValue;
246     releaseThisandParentDOM();
247     DateTime* ret = new DateTime(newValue);
248     if (duration)
249         ret->parseDuration();
250     else
251         ret->parseDateTime();
252     return ret;
253 }
254
255 XMLObject* AbstractXMLObject::prepareForAssignment(XMLObject* oldValue, XMLObject* newValue)
256 {
257     if (newValue && newValue->hasParent())
258         throw XMLObjectException("child XMLObject cannot be added - it is already the child of another XMLObject");
259
260     if (!oldValue) {
261         if (newValue) {
262             releaseThisandParentDOM();
263             newValue->setParent(this);
264         }
265         return newValue;
266     }
267
268     if (oldValue != newValue) {
269         delete oldValue;
270         releaseThisandParentDOM();
271         if (newValue)
272             newValue->setParent(this);
273     }
274
275     return newValue;
276 }