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 
31 #include <algorithm>
32 
33 using namespace xmltooling;
34 using std::set;
35 
36 using xercesc::XMLString;
37 using xercesc::XMLDateTime;
38 using xercesc::XMLException;
39 
XMLObject()40 XMLObject::XMLObject()
41 {
42 }
43 
~XMLObject()44 XMLObject::~XMLObject()
45 {
46 }
47 
releaseThisandParentDOM() const48 void XMLObject::releaseThisandParentDOM() const
49 {
50     releaseDOM();
51     releaseParentDOM(true);
52 }
53 
releaseThisAndChildrenDOM() const54 void XMLObject::releaseThisAndChildrenDOM() const
55 {
56     releaseChildrenDOM(true);
57     releaseDOM();
58 }
59 
getLang() const60 const XMLCh* XMLObject::getLang() const
61 {
62     return nullptr;
63 }
64 
setNil(const XMLCh * value)65 void XMLObject::setNil(const XMLCh* value)
66 {
67     if (value) {
68         switch (*value) {
69             case xercesc::chLatin_t:
70                 nil(xmlconstants::XML_BOOL_TRUE);
71                 break;
72             case xercesc::chLatin_f:
73                 nil(xmlconstants::XML_BOOL_FALSE);
74                 break;
75             case xercesc::chDigit_1:
76                 nil(xmlconstants::XML_BOOL_ONE);
77                 break;
78             case xercesc::chDigit_0:
79                 nil(xmlconstants::XML_BOOL_ZERO);
80                 break;
81             default:
82                 nil(xmlconstants::XML_BOOL_NULL);
83         }
84     }
85     else {
86         nil(xmlconstants::XML_BOOL_NULL);
87     }
88 }
89 
AbstractXMLObject(const XMLCh * nsURI,const XMLCh * localName,const XMLCh * prefix,const QName * schemaType)90 AbstractXMLObject::AbstractXMLObject(const XMLCh* nsURI, const XMLCh* localName, const XMLCh* prefix, const QName* schemaType)
91     : m_log(logging::Category::getInstance(XMLTOOLING_LOGCAT ".XMLObject")),
92     	m_schemaLocation(nullptr), m_noNamespaceSchemaLocation(nullptr), m_nil(xmlconstants::XML_BOOL_NULL),
93         m_parent(nullptr), m_elementQname(nsURI, localName, prefix)
94 {
95     addNamespace(Namespace(nsURI, prefix, false, Namespace::VisiblyUsed));
96     if (schemaType) {
97         m_typeQname.reset(new QName(*schemaType));
98         addNamespace(Namespace(m_typeQname->getNamespaceURI(), m_typeQname->getPrefix(), false, Namespace::NonVisiblyUsed));
99     }
100 }
101 
AbstractXMLObject(const AbstractXMLObject & src)102 AbstractXMLObject::AbstractXMLObject(const AbstractXMLObject& src)
103     : m_namespaces(src.m_namespaces), m_log(src.m_log), m_schemaLocation(XMLString::replicate(src.m_schemaLocation)),
104         m_noNamespaceSchemaLocation(XMLString::replicate(src.m_noNamespaceSchemaLocation)), m_nil(src.m_nil),
105         m_parent(nullptr), m_elementQname(src.m_elementQname),
106         m_typeQname(src.m_typeQname.get() ? new QName(*src.m_typeQname) : nullptr)
107 {
108 }
109 
~AbstractXMLObject()110 AbstractXMLObject::~AbstractXMLObject()
111 {
112     xercesc::XMLString::release(&m_schemaLocation);
113     xercesc::XMLString::release(&m_noNamespaceSchemaLocation);
114 }
115 
detach()116 void AbstractXMLObject::detach()
117 {
118     if (!getParent())
119         return;
120     else if (getParent()->hasParent())
121         throw XMLObjectException("Cannot detach an object whose parent is itself a child.");
122 
123     // Pull ourselves out of the parent and then blast him.
124     getParent()->removeChild(this);
125     delete m_parent;
126     m_parent = nullptr;
127 }
128 
getElementQName() const129 const QName& AbstractXMLObject::getElementQName() const
130 {
131     return m_elementQname;
132 }
133 
getNamespaces() const134 const set<Namespace>& AbstractXMLObject::getNamespaces() const
135 {
136     return m_namespaces;
137 }
138 
addNamespace(const Namespace & ns) const139 void AbstractXMLObject::addNamespace(const Namespace& ns) const
140 {
141     for (set<Namespace>::const_iterator n = m_namespaces.begin(); n != m_namespaces.end(); ++n) {
142         // Look for the prefix in the existing set.
143         if (XMLString::equals(ns.getNamespacePrefix(), n->getNamespacePrefix())) {
144             // See if it's the same declaration, and overlay various properties if so.
145             if (XMLString::equals(ns.getNamespaceURI(), n->getNamespaceURI())) {
146                 if (ns.alwaysDeclare())
147                     const_cast<Namespace&>(*n).setAlwaysDeclare(true);
148                 switch (ns.usage()) {
149                     case Namespace::Indeterminate:
150                         break;
151                     case Namespace::VisiblyUsed:
152                         const_cast<Namespace&>(*n).setUsage(Namespace::VisiblyUsed);
153                         break;
154                     case Namespace::NonVisiblyUsed:
155                         if (n->usage() == Namespace::Indeterminate)
156                             const_cast<Namespace&>(*n).setUsage(Namespace::NonVisiblyUsed);
157                         break;
158                 }
159             }
160             return;
161         }
162     }
163 
164     // If the prefix is now, go ahead and add it.
165     m_namespaces.insert(ns);
166 }
167 
removeNamespace(const Namespace & ns)168 void AbstractXMLObject::removeNamespace(const Namespace& ns)
169 {
170     m_namespaces.erase(ns);
171 }
172 
getSchemaType() const173 const QName* AbstractXMLObject::getSchemaType() const
174 {
175     return m_typeQname.get();
176 }
177 
getXMLID() const178 const XMLCh* AbstractXMLObject::getXMLID() const
179 {
180     return nullptr;
181 }
182 
getNil() const183 xmlconstants::xmltooling_bool_t AbstractXMLObject::getNil() const
184 {
185     return m_nil;
186 }
187 
nil(xmlconstants::xmltooling_bool_t value)188 void AbstractXMLObject::nil(xmlconstants::xmltooling_bool_t value)
189 {
190     if (m_nil != value) {
191         releaseThisandParentDOM();
192         m_nil = value;
193     }
194 }
195 
hasParent() const196 bool AbstractXMLObject::hasParent() const
197 {
198     return m_parent != nullptr;
199 }
200 
getParent() const201 XMLObject* AbstractXMLObject::getParent() const
202 {
203     return m_parent;
204 }
205 
setParent(XMLObject * parent)206 void AbstractXMLObject::setParent(XMLObject* parent)
207 {
208     m_parent = parent;
209 }
210 
prepareForAssignment(XMLCh * oldValue,const XMLCh * newValue)211 XMLCh* AbstractXMLObject::prepareForAssignment(XMLCh* oldValue, const XMLCh* newValue)
212 {
213     if (!XMLString::equals(oldValue,newValue)) {
214         releaseThisandParentDOM();
215         XMLCh* newString = XMLString::replicate(newValue);
216         XMLString::release(&oldValue);
217         return newString;
218     }
219     return oldValue;
220 }
221 
prepareForAssignment(QName * oldValue,const QName * newValue)222 QName* AbstractXMLObject::prepareForAssignment(QName* oldValue, const QName* newValue)
223 {
224     if (!oldValue) {
225         if (newValue) {
226             releaseThisandParentDOM();
227             addNamespace(Namespace(newValue->getNamespaceURI(), newValue->getPrefix(), false, Namespace::NonVisiblyUsed));
228             return new QName(*newValue);
229         }
230         return nullptr;
231     }
232 
233     delete oldValue;
234     releaseThisandParentDOM();
235     if (newValue) {
236         // Attach a non-visibly used namespace.
237         addNamespace(Namespace(newValue->getNamespaceURI(), newValue->getPrefix(), false, Namespace::NonVisiblyUsed));
238         return new QName(*newValue);
239     }
240     return nullptr;
241 }
242 
prepareForAssignment(XMLDateTime * oldValue,const XMLDateTime * newValue)243 XMLDateTime* AbstractXMLObject::prepareForAssignment(XMLDateTime* oldValue, const XMLDateTime* newValue)
244 {
245     if (!oldValue) {
246         if (newValue) {
247             releaseThisandParentDOM();
248             try {
249                 return new XMLDateTime(*newValue);
250             }
251             catch (const XMLException& e) {
252                 auto_ptr_char temp(e.getMessage());
253                 throw XMLObjectException(temp.get() ? temp.get() : "XMLException duplicating XMLDateTime object");
254             }
255         }
256         return nullptr;
257     }
258 
259     releaseThisandParentDOM();
260 
261     // Avoid deleting existing object until new one is safely created.
262     XMLDateTime* ret = nullptr;
263     try {
264         if (newValue)
265             ret = new XMLDateTime(*newValue);
266     }
267     catch (const XMLException& e) {
268         auto_ptr_char temp(e.getMessage());
269         throw XMLObjectException(temp.get() ? temp.get() : "XMLException duplicating XMLDateTime object");
270     }
271 
272     delete oldValue;
273     return ret;
274 }
275 
prepareForAssignment(XMLDateTime * oldValue,time_t newValue,bool duration)276 XMLDateTime* AbstractXMLObject::prepareForAssignment(XMLDateTime* oldValue, time_t newValue, bool duration)
277 {
278     // Avoid deleting existing object until new one is safely created.
279     XMLDateTime* ret = nullptr;
280     try {
281         ret = new XMLDateTime(newValue, duration);
282         if (duration)
283             ret->parseDuration();
284         else
285             ret->parseDateTime();
286     }
287     catch (const XMLException& e) {
288         auto_ptr_char temp(e.getMessage());
289         throw XMLObjectException(temp.get() ? temp.get() : "XMLException creating XMLDateTime object");
290     }
291 
292     delete oldValue;
293     releaseThisandParentDOM();
294     return ret;
295 }
296 
prepareForAssignment(XMLDateTime * oldValue,const XMLCh * newValue,bool duration)297 XMLDateTime* AbstractXMLObject::prepareForAssignment(XMLDateTime* oldValue, const XMLCh* newValue, bool duration)
298 {
299     if (!newValue || !*newValue) {
300         delete oldValue;
301         releaseThisandParentDOM();
302         return nullptr;
303     }
304 
305     // Avoid deleting existing object until new one is safely created.
306     XMLDateTime* ret = nullptr;
307     try {
308         ret = new XMLDateTime(newValue);
309         if (duration)
310             ret->parseDuration();
311         else
312             ret->parseDateTime();
313     }
314     catch (const XMLException& e) {
315         auto_ptr_char temp(e.getMessage());
316         throw XMLObjectException(temp.get() ? temp.get() : "XMLException creating XMLDateTime object");
317     }
318 
319     delete oldValue;
320     releaseThisandParentDOM();
321     return ret;
322 }
323 
prepareForAssignment(XMLObject * oldValue,XMLObject * newValue)324 XMLObject* AbstractXMLObject::prepareForAssignment(XMLObject* oldValue, XMLObject* newValue)
325 {
326     if (newValue && newValue->hasParent())
327         throw XMLObjectException("child XMLObject cannot be added - it is already the child of another XMLObject");
328 
329     if (!oldValue) {
330         if (newValue) {
331             releaseThisandParentDOM();
332             newValue->setParent(this);
333         }
334         return newValue;
335     }
336 
337     if (oldValue != newValue) {
338         delete oldValue;
339         releaseThisandParentDOM();
340         if (newValue)
341             newValue->setParent(this);
342     }
343 
344     return newValue;
345 }
346