Reimplement namespace visibility as a three-way setting.
[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, 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(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 {
137         if (ns.alwaysDeclare())
138             const_cast<Namespace&>(*i).setAlwaysDeclare(true);
139         switch (ns.usage()) {
140             case Namespace::Indeterminate:
141                 break;
142             case Namespace::VisiblyUsed:
143                 const_cast<Namespace&>(*i).setUsage(Namespace::VisiblyUsed);
144                 break;
145             case Namespace::NonVisiblyUsed:
146                 if (i->usage() == Namespace::Indeterminate)
147                     const_cast<Namespace&>(*i).setUsage(Namespace::NonVisiblyUsed);
148                 break;
149         }
150     }
151 }
152
153 void AbstractXMLObject::removeNamespace(const Namespace& ns)
154 {
155     m_namespaces.erase(ns);
156 }
157
158 const QName* AbstractXMLObject::getSchemaType() const
159 {
160     return m_typeQname;
161 }
162
163 const XMLCh* AbstractXMLObject::getXMLID() const
164 {
165     return NULL;
166 }
167
168 xmlconstants::xmltooling_bool_t AbstractXMLObject::getNil() const
169 {
170     return m_nil;
171 }
172
173 void AbstractXMLObject::nil(xmlconstants::xmltooling_bool_t value)
174 {
175     if (m_nil != value) {
176         releaseThisandParentDOM();
177         m_nil = value;
178     }
179 }
180
181 bool AbstractXMLObject::hasParent() const
182 {
183     return m_parent != NULL;
184 }
185
186 XMLObject* AbstractXMLObject::getParent() const
187 {
188     return m_parent;
189 }
190
191 void AbstractXMLObject::setParent(XMLObject* parent)
192 {
193     m_parent = parent;
194 }
195
196 XMLCh* AbstractXMLObject::prepareForAssignment(XMLCh* oldValue, const XMLCh* newValue)
197 {
198     if (!XMLString::equals(oldValue,newValue)) {
199         releaseThisandParentDOM();
200         XMLCh* newString = XMLString::replicate(newValue);
201         XMLString::release(&oldValue);
202         return newString;
203     }
204     return oldValue;
205 }
206
207 QName* AbstractXMLObject::prepareForAssignment(QName* oldValue, const QName* newValue)
208 {
209     if (!oldValue) {
210         if (newValue) {
211             releaseThisandParentDOM();
212             addNamespace(Namespace(newValue->getNamespaceURI(), newValue->getPrefix(), false, Namespace::NonVisiblyUsed));
213             return new QName(*newValue);
214         }
215         return NULL;
216     }
217
218     delete oldValue;
219     releaseThisandParentDOM();
220     if (newValue) {
221         // Attach a non-visibly used namespace.
222         addNamespace(Namespace(newValue->getNamespaceURI(), newValue->getPrefix(), false, Namespace::NonVisiblyUsed));
223         return new QName(*newValue);
224     }
225     return NULL;
226 }
227
228 DateTime* AbstractXMLObject::prepareForAssignment(DateTime* oldValue, const DateTime* newValue)
229 {
230     if (!oldValue) {
231         if (newValue) {
232             releaseThisandParentDOM();
233             return new DateTime(*newValue);
234         }
235         return NULL;
236     }
237
238     delete oldValue;
239     releaseThisandParentDOM();
240     return newValue ? new DateTime(*newValue) : NULL;
241 }
242
243 DateTime* AbstractXMLObject::prepareForAssignment(DateTime* oldValue, time_t newValue, bool duration)
244 {
245     delete oldValue;
246     releaseThisandParentDOM();
247     DateTime* ret = new DateTime(newValue, duration);
248     if (duration)
249         ret->parseDuration();
250     else
251         ret->parseDateTime();
252     return ret;
253 }
254
255 DateTime* AbstractXMLObject::prepareForAssignment(DateTime* oldValue, const XMLCh* newValue, bool duration)
256 {
257     delete oldValue;
258     releaseThisandParentDOM();
259     if (!newValue || !*newValue)
260         return NULL;
261     DateTime* ret = new DateTime(newValue);
262     if (duration)
263         ret->parseDuration();
264     else
265         ret->parseDateTime();
266     return ret;
267 }
268
269 XMLObject* AbstractXMLObject::prepareForAssignment(XMLObject* oldValue, XMLObject* newValue)
270 {
271     if (newValue && newValue->hasParent())
272         throw XMLObjectException("child XMLObject cannot be added - it is already the child of another XMLObject");
273
274     if (!oldValue) {
275         if (newValue) {
276             releaseThisandParentDOM();
277             newValue->setParent(this);
278         }
279         return newValue;
280     }
281
282     if (oldValue != newValue) {
283         delete oldValue;
284         releaseThisandParentDOM();
285         if (newValue)
286             newValue->setParent(this);
287     }
288
289     return newValue;
290 }