Distinguish between visibly used and unused namespaces.
[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(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         // Attach a non-visibly used namespace.
64         addNamespace(Namespace(m_typeQname->getNamespaceURI(), m_typeQname->getPrefix(), false, false));
65     }
66 }
67
68 AbstractXMLObject::AbstractXMLObject(const AbstractXMLObject& src)
69     : m_namespaces(src.m_namespaces), m_log(src.m_log), m_schemaLocation(XMLString::replicate(src.m_schemaLocation)),
70         m_noNamespaceSchemaLocation(XMLString::replicate(src.m_noNamespaceSchemaLocation)), m_nil(src.m_nil),
71         m_parent(NULL), m_elementQname(src.m_elementQname), m_typeQname(NULL)
72 {
73     if (src.m_typeQname)
74         m_typeQname=new QName(*src.m_typeQname);
75 }
76
77 AbstractXMLObject::~AbstractXMLObject()
78 {
79     delete m_typeQname;
80     xercesc::XMLString::release(&m_schemaLocation);
81     xercesc::XMLString::release(&m_noNamespaceSchemaLocation);
82 }
83
84 void AbstractXMLObject::detach()
85 {
86     if (!getParent())
87         return;
88     else if (getParent()->hasParent())
89         throw XMLObjectException("Cannot detach an object whose parent is itself a child.");
90
91     // Pull ourselves out of the parent and then blast him.
92     getParent()->removeChild(this);
93     delete m_parent;
94     m_parent = NULL;
95 }
96
97 const QName& AbstractXMLObject::getElementQName() const
98 {
99     return m_elementQname;
100 }
101
102 const set<Namespace>& AbstractXMLObject::getNamespaces() const
103 {
104     return m_namespaces;
105 }
106
107 void XMLObject::setNil(const XMLCh* value)
108 {
109     if (value) {
110         switch (*value) {
111             case xercesc::chLatin_t:
112                 nil(xmlconstants::XML_BOOL_TRUE);
113                 break;
114             case xercesc::chLatin_f:
115                 nil(xmlconstants::XML_BOOL_FALSE);
116                 break;
117             case xercesc::chDigit_1:
118                 nil(xmlconstants::XML_BOOL_ONE);
119                 break;
120             case xercesc::chDigit_0:
121                 nil(xmlconstants::XML_BOOL_ZERO);
122                 break;
123             default:
124                 nil(xmlconstants::XML_BOOL_NULL);
125         }
126     }
127     else {
128         nil(xmlconstants::XML_BOOL_NULL);
129     }
130 }
131
132 void AbstractXMLObject::addNamespace(const Namespace& ns) const
133 {
134     std::set<Namespace>::iterator i = m_namespaces.find(ns);
135     if (i == m_namespaces.end())
136         m_namespaces.insert(ns);
137     else {
138         if (ns.alwaysDeclare())
139             const_cast<Namespace&>(*i).setAlwaysDeclare(true);
140         if (ns.visiblyUsed())
141             const_cast<Namespace&>(*i).setVisiblyUsed(true);
142     }
143 }
144
145 void AbstractXMLObject::removeNamespace(const Namespace& ns)
146 {
147     m_namespaces.erase(ns);
148 }
149
150 const QName* AbstractXMLObject::getSchemaType() const
151 {
152     return m_typeQname;
153 }
154
155 const XMLCh* AbstractXMLObject::getXMLID() const
156 {
157     return NULL;
158 }
159
160 xmlconstants::xmltooling_bool_t AbstractXMLObject::getNil() const
161 {
162     return m_nil;
163 }
164
165 void AbstractXMLObject::nil(xmlconstants::xmltooling_bool_t value)
166 {
167     if (m_nil != value) {
168         releaseThisandParentDOM();
169         m_nil = value;
170     }
171 }
172
173 bool AbstractXMLObject::hasParent() const
174 {
175     return m_parent != NULL;
176 }
177
178 XMLObject* AbstractXMLObject::getParent() const
179 {
180     return m_parent;
181 }
182
183 void AbstractXMLObject::setParent(XMLObject* parent)
184 {
185     m_parent = parent;
186 }
187
188 XMLCh* AbstractXMLObject::prepareForAssignment(XMLCh* oldValue, const XMLCh* newValue)
189 {
190     if (!XMLString::equals(oldValue,newValue)) {
191         releaseThisandParentDOM();
192         XMLCh* newString = XMLString::replicate(newValue);
193         XMLString::release(&oldValue);
194         return newString;
195     }
196     return oldValue;
197 }
198
199 QName* AbstractXMLObject::prepareForAssignment(QName* oldValue, const QName* newValue)
200 {
201     if (!oldValue) {
202         if (newValue) {
203             releaseThisandParentDOM();
204             // Attach a non-visibly used namespace.
205             addNamespace(Namespace(newValue->getNamespaceURI(), newValue->getPrefix(), false, false));
206             return new QName(*newValue);
207         }
208         return NULL;
209     }
210
211     delete oldValue;
212     releaseThisandParentDOM();
213     if (newValue) {
214         // Attach a non-visibly used namespace.
215         addNamespace(Namespace(newValue->getNamespaceURI(), newValue->getPrefix(), false, false));
216         return new QName(*newValue);
217     }
218     return NULL;
219 }
220
221 DateTime* AbstractXMLObject::prepareForAssignment(DateTime* oldValue, const DateTime* newValue)
222 {
223     if (!oldValue) {
224         if (newValue) {
225             releaseThisandParentDOM();
226             return new DateTime(*newValue);
227         }
228         return NULL;
229     }
230
231     delete oldValue;
232     releaseThisandParentDOM();
233     return newValue ? new DateTime(*newValue) : NULL;
234 }
235
236 DateTime* AbstractXMLObject::prepareForAssignment(DateTime* oldValue, time_t newValue, bool duration)
237 {
238     delete oldValue;
239     releaseThisandParentDOM();
240     DateTime* ret = new DateTime(newValue, duration);
241     if (duration)
242         ret->parseDuration();
243     else
244         ret->parseDateTime();
245     return ret;
246 }
247
248 DateTime* AbstractXMLObject::prepareForAssignment(DateTime* oldValue, const XMLCh* newValue, bool duration)
249 {
250     delete oldValue;
251     releaseThisandParentDOM();
252     if (!newValue || !*newValue)
253         return NULL;
254     DateTime* ret = new DateTime(newValue);
255     if (duration)
256         ret->parseDuration();
257     else
258         ret->parseDateTime();
259     return ret;
260 }
261
262 XMLObject* AbstractXMLObject::prepareForAssignment(XMLObject* oldValue, XMLObject* newValue)
263 {
264     if (newValue && newValue->hasParent())
265         throw XMLObjectException("child XMLObject cannot be added - it is already the child of another XMLObject");
266
267     if (!oldValue) {
268         if (newValue) {
269             releaseThisandParentDOM();
270             newValue->setParent(this);
271         }
272         return newValue;
273     }
274
275     if (oldValue != newValue) {
276         delete oldValue;
277         releaseThisandParentDOM();
278         if (newValue)
279             newValue->setParent(this);
280     }
281
282     return newValue;
283 }