b7c3cac748f4534ebaa8916e25ea7c56f90cb5be
[shibboleth/cpp-xmltooling.git] / xmltooling / AbstractXMLObject.cpp
1 /*
2 *  Copyright 2001-2010 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(nullptr), m_noNamespaceSchemaLocation(nullptr), m_nil(xmlconstants::XML_BOOL_NULL),
58         m_parent(nullptr), m_elementQname(nsURI, localName, prefix), m_typeQname(nullptr)
59 {
60     addNamespace(Namespace(nsURI, prefix, false, Namespace::VisiblyUsed));
61     if (schemaType) {
62         m_typeQname = new QName(*schemaType);
63         addNamespace(Namespace(m_typeQname->getNamespaceURI(), m_typeQname->getPrefix(), false, Namespace::NonVisiblyUsed));
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(nullptr), m_elementQname(src.m_elementQname), m_typeQname(nullptr)
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 = nullptr;
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     for (set<Namespace>::const_iterator n = m_namespaces.begin(); n != m_namespaces.end(); ++n) {
134         // Look for the prefix in the existing set.
135         if (XMLString::equals(ns.getNamespacePrefix(), n->getNamespacePrefix())) {
136             // See if it's the same declaration, and overlay various properties if so.
137             if (XMLString::equals(ns.getNamespaceURI(), n->getNamespaceURI())) {
138                 if (ns.alwaysDeclare())
139                     const_cast<Namespace&>(*n).setAlwaysDeclare(true);
140                 switch (ns.usage()) {
141                     case Namespace::Indeterminate:
142                         break;
143                     case Namespace::VisiblyUsed:
144                         const_cast<Namespace&>(*n).setUsage(Namespace::VisiblyUsed);
145                         break;
146                     case Namespace::NonVisiblyUsed:
147                         if (n->usage() == Namespace::Indeterminate)
148                             const_cast<Namespace&>(*n).setUsage(Namespace::NonVisiblyUsed);
149                         break;
150                 }
151             }
152             return;
153         }
154     }
155
156     // If the prefix is now, go ahead and add it.
157     m_namespaces.insert(ns);
158 }
159
160 void AbstractXMLObject::removeNamespace(const Namespace& ns)
161 {
162     m_namespaces.erase(ns);
163 }
164
165 const QName* AbstractXMLObject::getSchemaType() const
166 {
167     return m_typeQname;
168 }
169
170 const XMLCh* AbstractXMLObject::getXMLID() const
171 {
172     return nullptr;
173 }
174
175 xmlconstants::xmltooling_bool_t AbstractXMLObject::getNil() const
176 {
177     return m_nil;
178 }
179
180 void AbstractXMLObject::nil(xmlconstants::xmltooling_bool_t value)
181 {
182     if (m_nil != value) {
183         releaseThisandParentDOM();
184         m_nil = value;
185     }
186 }
187
188 bool AbstractXMLObject::hasParent() const
189 {
190     return m_parent != nullptr;
191 }
192
193 XMLObject* AbstractXMLObject::getParent() const
194 {
195     return m_parent;
196 }
197
198 void AbstractXMLObject::setParent(XMLObject* parent)
199 {
200     m_parent = parent;
201 }
202
203 XMLCh* AbstractXMLObject::prepareForAssignment(XMLCh* oldValue, const XMLCh* newValue)
204 {
205     if (!XMLString::equals(oldValue,newValue)) {
206         releaseThisandParentDOM();
207         XMLCh* newString = XMLString::replicate(newValue);
208         XMLString::release(&oldValue);
209         return newString;
210     }
211     return oldValue;
212 }
213
214 QName* AbstractXMLObject::prepareForAssignment(QName* oldValue, const QName* newValue)
215 {
216     if (!oldValue) {
217         if (newValue) {
218             releaseThisandParentDOM();
219             addNamespace(Namespace(newValue->getNamespaceURI(), newValue->getPrefix(), false, Namespace::NonVisiblyUsed));
220             return new QName(*newValue);
221         }
222         return nullptr;
223     }
224
225     delete oldValue;
226     releaseThisandParentDOM();
227     if (newValue) {
228         // Attach a non-visibly used namespace.
229         addNamespace(Namespace(newValue->getNamespaceURI(), newValue->getPrefix(), false, Namespace::NonVisiblyUsed));
230         return new QName(*newValue);
231     }
232     return nullptr;
233 }
234
235 DateTime* AbstractXMLObject::prepareForAssignment(DateTime* oldValue, const DateTime* newValue)
236 {
237     if (!oldValue) {
238         if (newValue) {
239             releaseThisandParentDOM();
240             return new DateTime(*newValue);
241         }
242         return nullptr;
243     }
244
245     delete oldValue;
246     releaseThisandParentDOM();
247     return newValue ? new DateTime(*newValue) : nullptr;
248 }
249
250 DateTime* AbstractXMLObject::prepareForAssignment(DateTime* oldValue, time_t newValue, bool duration)
251 {
252     delete oldValue;
253     releaseThisandParentDOM();
254     DateTime* ret = new DateTime(newValue, duration);
255     if (duration)
256         ret->parseDuration();
257     else
258         ret->parseDateTime();
259     return ret;
260 }
261
262 DateTime* AbstractXMLObject::prepareForAssignment(DateTime* oldValue, const XMLCh* newValue, bool duration)
263 {
264     delete oldValue;
265     releaseThisandParentDOM();
266     if (!newValue || !*newValue)
267         return nullptr;
268     DateTime* ret = new DateTime(newValue);
269     if (duration)
270         ret->parseDuration();
271     else
272         ret->parseDateTime();
273     return ret;
274 }
275
276 XMLObject* AbstractXMLObject::prepareForAssignment(XMLObject* oldValue, XMLObject* newValue)
277 {
278     if (newValue && newValue->hasParent())
279         throw XMLObjectException("child XMLObject cannot be added - it is already the child of another XMLObject");
280
281     if (!oldValue) {
282         if (newValue) {
283             releaseThisandParentDOM();
284             newValue->setParent(this);
285         }
286         return newValue;
287     }
288
289     if (oldValue != newValue) {
290         delete oldValue;
291         releaseThisandParentDOM();
292         if (newValue)
293             newValue->setParent(this);
294     }
295
296     return newValue;
297 }