Set fourth file version digit to signify rebuild.
[shibboleth/cpp-xmltooling.git] / xmltooling / AbstractXMLObject.cpp
1 /**
2  * Licensed to the University Corporation for Advanced Internet
3  * Development, Inc. (UCAID) under one or more contributor license
4  * agreements. See the NOTICE file distributed with this work for
5  * additional information regarding copyright ownership.
6  *
7  * UCAID licenses this file to you under the Apache License,
8  * Version 2.0 (the "License"); you may not use this file except
9  * in compliance with the License. You may obtain a copy of the
10  * License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
17  * either express or implied. See the License for the specific
18  * language governing permissions and limitations under the License.
19  */
20
21 /**
22  * AbstractXMLObject.cpp
23  *
24  * An abstract implementation of XMLObject.
25  */
26
27 #include "internal.h"
28 #include "exceptions.h"
29 #include "AbstractXMLObject.h"
30 #include "util/DateTime.h"
31
32 #include <algorithm>
33
34 using namespace xmltooling;
35 using std::set;
36
37 using xercesc::XMLString;
38
39 XMLObject::XMLObject()
40 {
41 }
42
43 XMLObject::~XMLObject()
44 {
45 }
46
47 void XMLObject::releaseThisandParentDOM() const
48 {
49     releaseDOM();
50     releaseParentDOM(true);
51 }
52
53 void XMLObject::releaseThisAndChildrenDOM() const
54 {
55     releaseChildrenDOM(true);
56     releaseDOM();
57 }
58
59 const XMLCh* XMLObject::getLang() const
60 {
61     return nullptr;
62 }
63
64 void XMLObject::setNil(const XMLCh* value)
65 {
66     if (value) {
67         switch (*value) {
68             case xercesc::chLatin_t:
69                 nil(xmlconstants::XML_BOOL_TRUE);
70                 break;
71             case xercesc::chLatin_f:
72                 nil(xmlconstants::XML_BOOL_FALSE);
73                 break;
74             case xercesc::chDigit_1:
75                 nil(xmlconstants::XML_BOOL_ONE);
76                 break;
77             case xercesc::chDigit_0:
78                 nil(xmlconstants::XML_BOOL_ZERO);
79                 break;
80             default:
81                 nil(xmlconstants::XML_BOOL_NULL);
82         }
83     }
84     else {
85         nil(xmlconstants::XML_BOOL_NULL);
86     }
87 }
88
89 AbstractXMLObject::AbstractXMLObject(const XMLCh* nsURI, const XMLCh* localName, const XMLCh* prefix, const QName* schemaType)
90     : m_log(logging::Category::getInstance(XMLTOOLING_LOGCAT ".XMLObject")),
91         m_schemaLocation(nullptr), m_noNamespaceSchemaLocation(nullptr), m_nil(xmlconstants::XML_BOOL_NULL),
92         m_parent(nullptr), m_elementQname(nsURI, localName, prefix)
93 {
94     addNamespace(Namespace(nsURI, prefix, false, Namespace::VisiblyUsed));
95     if (schemaType) {
96         m_typeQname.reset(new QName(*schemaType));
97         addNamespace(Namespace(m_typeQname->getNamespaceURI(), m_typeQname->getPrefix(), false, Namespace::NonVisiblyUsed));
98     }
99 }
100
101 AbstractXMLObject::AbstractXMLObject(const AbstractXMLObject& src)
102     : m_namespaces(src.m_namespaces), m_log(src.m_log), m_schemaLocation(XMLString::replicate(src.m_schemaLocation)),
103         m_noNamespaceSchemaLocation(XMLString::replicate(src.m_noNamespaceSchemaLocation)), m_nil(src.m_nil),
104         m_parent(nullptr), m_elementQname(src.m_elementQname),
105         m_typeQname(src.m_typeQname.get() ? new QName(*src.m_typeQname) : nullptr)
106 {
107 }
108
109 AbstractXMLObject::~AbstractXMLObject()
110 {
111     xercesc::XMLString::release(&m_schemaLocation);
112     xercesc::XMLString::release(&m_noNamespaceSchemaLocation);
113 }
114
115 void AbstractXMLObject::detach()
116 {
117     if (!getParent())
118         return;
119     else if (getParent()->hasParent())
120         throw XMLObjectException("Cannot detach an object whose parent is itself a child.");
121
122     // Pull ourselves out of the parent and then blast him.
123     getParent()->removeChild(this);
124     delete m_parent;
125     m_parent = nullptr;
126 }
127
128 const QName& AbstractXMLObject::getElementQName() const
129 {
130     return m_elementQname;
131 }
132
133 const set<Namespace>& AbstractXMLObject::getNamespaces() const
134 {
135     return m_namespaces;
136 }
137
138 void AbstractXMLObject::addNamespace(const Namespace& ns) const
139 {
140     for (set<Namespace>::const_iterator n = m_namespaces.begin(); n != m_namespaces.end(); ++n) {
141         // Look for the prefix in the existing set.
142         if (XMLString::equals(ns.getNamespacePrefix(), n->getNamespacePrefix())) {
143             // See if it's the same declaration, and overlay various properties if so.
144             if (XMLString::equals(ns.getNamespaceURI(), n->getNamespaceURI())) {
145                 if (ns.alwaysDeclare())
146                     const_cast<Namespace&>(*n).setAlwaysDeclare(true);
147                 switch (ns.usage()) {
148                     case Namespace::Indeterminate:
149                         break;
150                     case Namespace::VisiblyUsed:
151                         const_cast<Namespace&>(*n).setUsage(Namespace::VisiblyUsed);
152                         break;
153                     case Namespace::NonVisiblyUsed:
154                         if (n->usage() == Namespace::Indeterminate)
155                             const_cast<Namespace&>(*n).setUsage(Namespace::NonVisiblyUsed);
156                         break;
157                 }
158             }
159             return;
160         }
161     }
162
163     // If the prefix is now, go ahead and add it.
164     m_namespaces.insert(ns);
165 }
166
167 void AbstractXMLObject::removeNamespace(const Namespace& ns)
168 {
169     m_namespaces.erase(ns);
170 }
171
172 const QName* AbstractXMLObject::getSchemaType() const
173 {
174     return m_typeQname.get();
175 }
176
177 const XMLCh* AbstractXMLObject::getXMLID() const
178 {
179     return nullptr;
180 }
181
182 xmlconstants::xmltooling_bool_t AbstractXMLObject::getNil() const
183 {
184     return m_nil;
185 }
186
187 void AbstractXMLObject::nil(xmlconstants::xmltooling_bool_t value)
188 {
189     if (m_nil != value) {
190         releaseThisandParentDOM();
191         m_nil = value;
192     }
193 }
194
195 bool AbstractXMLObject::hasParent() const
196 {
197     return m_parent != nullptr;
198 }
199
200 XMLObject* AbstractXMLObject::getParent() const
201 {
202     return m_parent;
203 }
204
205 void AbstractXMLObject::setParent(XMLObject* parent)
206 {
207     m_parent = parent;
208 }
209
210 XMLCh* AbstractXMLObject::prepareForAssignment(XMLCh* oldValue, const XMLCh* newValue)
211 {
212     if (!XMLString::equals(oldValue,newValue)) {
213         releaseThisandParentDOM();
214         XMLCh* newString = XMLString::replicate(newValue);
215         XMLString::release(&oldValue);
216         return newString;
217     }
218     return oldValue;
219 }
220
221 QName* AbstractXMLObject::prepareForAssignment(QName* oldValue, const QName* newValue)
222 {
223     if (!oldValue) {
224         if (newValue) {
225             releaseThisandParentDOM();
226             addNamespace(Namespace(newValue->getNamespaceURI(), newValue->getPrefix(), false, Namespace::NonVisiblyUsed));
227             return new QName(*newValue);
228         }
229         return nullptr;
230     }
231
232     delete oldValue;
233     releaseThisandParentDOM();
234     if (newValue) {
235         // Attach a non-visibly used namespace.
236         addNamespace(Namespace(newValue->getNamespaceURI(), newValue->getPrefix(), false, Namespace::NonVisiblyUsed));
237         return new QName(*newValue);
238     }
239     return nullptr;
240 }
241
242 DateTime* AbstractXMLObject::prepareForAssignment(DateTime* oldValue, const DateTime* newValue)
243 {
244     if (!oldValue) {
245         if (newValue) {
246             releaseThisandParentDOM();
247             return new DateTime(*newValue);
248         }
249         return nullptr;
250     }
251
252     delete oldValue;
253     releaseThisandParentDOM();
254     return newValue ? new DateTime(*newValue) : nullptr;
255 }
256
257 DateTime* AbstractXMLObject::prepareForAssignment(DateTime* oldValue, time_t newValue, bool duration)
258 {
259     delete oldValue;
260     releaseThisandParentDOM();
261     DateTime* ret = new DateTime(newValue, duration);
262     if (duration)
263         ret->parseDuration();
264     else
265         ret->parseDateTime();
266     return ret;
267 }
268
269 DateTime* AbstractXMLObject::prepareForAssignment(DateTime* oldValue, const XMLCh* newValue, bool duration)
270 {
271     delete oldValue;
272     releaseThisandParentDOM();
273     if (!newValue || !*newValue)
274         return nullptr;
275     DateTime* ret = new DateTime(newValue);
276     if (duration)
277         ret->parseDuration();
278     else
279         ret->parseDateTime();
280     return ret;
281 }
282
283 XMLObject* AbstractXMLObject::prepareForAssignment(XMLObject* oldValue, XMLObject* newValue)
284 {
285     if (newValue && newValue->hasParent())
286         throw XMLObjectException("child XMLObject cannot be added - it is already the child of another XMLObject");
287
288     if (!oldValue) {
289         if (newValue) {
290             releaseThisandParentDOM();
291             newValue->setParent(this);
292         }
293         return newValue;
294     }
295
296     if (oldValue != newValue) {
297         delete oldValue;
298         releaseThisandParentDOM();
299         if (newValue)
300             newValue->setParent(this);
301     }
302
303     return newValue;
304 }