2 Copyright (C) 2000-2005 SKYRIX Software AG
4 This file is part of SOPE.
6 SOPE is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with SOPE; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
22 #include "DOMXMLOutputter.h"
23 #include "DOMDocument.h"
24 #include "DOMElement.h"
27 @interface DOMXMLOutputter(Privates)
28 - (void)outputNode:(id<DOMNode>)_node to:(id)_target;
29 - (void)outputNodeList:(id<DOMNodeList>)_nodeList to:(id)_target;
32 @interface DOMXMLOutputter(PrefixStack)
33 - (NSString *)topPrefix;
34 - (NSString *)topNamespace;
35 - (void)pushPrefix:(NSString *)_prefix namespace:(NSString *)_namespace;
36 - (void)popPrefixAndNamespace;
37 - (BOOL)isTagValidInStack:(id)_node;
38 - (NSArray *)newAttributePrefixesAndNamespaces:(NSArray *)_attrs;
39 @end /* DOMXMLOutputter(PrefixStack) */
42 @implementation DOMXMLOutputter
45 if ((self = [super init])) {
46 self->stack = [[NSMutableArray alloc] initWithCapacity:32];
52 [self->stack release];
56 - (void)indentOn:(id)_target {
59 for (i = 0; i < (self->indent * 4); i++) {
61 [_target appendString:@" "];
67 - (void)write:(NSString *)s to:(id)_target {
69 [_target appendString:s];
71 printf("%s", [s cString]);
73 - (BOOL)currentElementPreservesWhitespace {
77 - (void)outputAttributeNode:(id<DOMAttr>)_attrNode
78 ofNode:(id<DOMNode>)_node
81 if ([[_attrNode prefix] length] > 0) {
82 [self write:[_attrNode prefix] to:_target];
83 [self write:@":" to:_target];
85 [self write:[_attrNode name] to:_target];
87 if ([_attrNode hasChildNodes]) {
91 [self write:@"=\"" to:_target];
93 children = [_attrNode childNodes];
94 for (i = 0, count = [children count]; i < count; i++) {
97 child = [children objectAtIndex:i];
99 if ([child nodeType] == DOM_TEXT_NODE)
100 [self write:[(id<DOMText>)child data] to:_target];
102 NSLog(@"WARNING: unsupported attribute value node %@", child);
105 [self write:@"\"" to:_target];
108 NSLog(@"WARNING: attribute %@ has no content !", _attrNode);
111 - (void)outputAttributeNodes:(id<DOMNamedNodeMap>)_nodes
112 list:(NSArray *)_list to:(id)_target
114 unsigned i, count, count2;
116 if ((count = [_nodes length]) == 0)
119 // append required prefix and namespaces
120 for (i = 0, count2 = [_list count]; i < count2; i = i + 2) {
121 [self write:@" xmlns:" to:_target];
122 [self write:[_list objectAtIndex:i] to:_target];
123 [self write:@"=\"" to:_target];
124 [self write:[_list objectAtIndex:i+1] to:_target];
125 [self write:@"\"" to:_target];
128 for (i = 0; i < count; i++) {
129 id<DOMAttr> attrNode;
131 attrNode = [_nodes objectAtIndex:i];
133 [self write:@" " to:_target];
134 [self outputAttributeNode:attrNode ofNode:nil to:_target];
138 - (void)outputTextNode:(id<DOMText>)_node to:(id)_target {
143 if ((len = [s length]) == 0)
146 if (![self currentElementPreservesWhitespace]) {
149 for (i = 0; i < len; i++) {
150 if (!isspace([s characterAtIndex:i]))
154 /* only whitespace */
157 [self indentOn:_target];
160 [self write:[_node data] to:_target];
162 if (![self currentElementPreservesWhitespace])
163 [self write:@"\n" to:_target];
165 - (void)outputCommentNode:(id<DOMComment>)_node to:(id)_target {
166 [self write:@"<!-- " to:_target];
167 [self write:[_node data] to:_target];
168 [self write:@" -->" to:_target];
170 if (![self currentElementPreservesWhitespace])
171 [self write:@"\n" to:_target];
174 - (void)outputElementNode:(id<DOMElement>)_node to:(id)_target {
175 NSArray *list; // new attribute prefixes and namespaces
183 // getting new attributes prefixes and namespaces
184 list = (NSArray *)[_node attributes];
185 list = [self newAttributePrefixesAndNamespaces:list];
187 // push new attribute prefixes and namespaces to stack
188 for (i = 0, count = [list count]; i < count; i = i + 2) {
189 [self pushPrefix:[list objectAtIndex:i]
190 namespace:[list objectAtIndex:i+1]];
193 tagURI = [_node namespaceURI];
194 tagPrefix = [_node prefix];
195 isNodeValid = [self isTagValidInStack:_node];
196 if (!isNodeValid) [self pushPrefix:tagPrefix namespace:tagURI];
198 /* needs to declare namespaces !!! */
199 tagName = [_node tagName];
200 if ([[_node prefix] length] > 0) {
204 ns = [NSString stringWithFormat:@" xmlns:%@=\"%@\"",
209 p = [p stringByAppendingString:@":"];
210 tagName = [p stringByAppendingString:tagName];
212 else if ([tagURI length] > 0) {
217 if ((parent = [_node parentNode])) {
218 if ([parent nodeType] == DOM_ELEMENT_NODE) {
219 if ([[parent namespaceURI] isEqualToString:tagURI]) {
220 if ([[parent prefix] length] == 0)
229 ns = [NSString stringWithFormat:@" xmlns=\"%@\"", [_node namespaceURI]];
236 if ([_node hasChildNodes]) {
237 [self indentOn:_target];
238 [self write:@"<" to:_target];
239 [self write:tagName to:_target];
240 if (ns) [self write:ns to:_target];
242 [self outputAttributeNodes:[_node attributes] list:list to:_target];
243 [self write:@">\n" to:_target];
246 [self outputNodeList:[_node childNodes] to:_target];
249 [self indentOn:_target];
250 [self write:@"</" to:_target];
251 [self write:tagName to:_target];
252 [self write:@">\n" to:_target];
255 [self indentOn:_target];
256 [self write:@"<" to:_target];
257 [self write:tagName to:_target];
258 [self outputAttributeNodes:[_node attributes] list:list to:_target];
259 [self write:@"/>\n" to:_target];
261 // pop attributes prefixes and namespaces from stack
262 for (i = 0; i < count; i = i + 2) {
263 [self popPrefixAndNamespace];
265 if (!isNodeValid) [self popPrefixAndNamespace];
268 - (void)outputCDATA:(id<DOMCharacterData>)_node to:(id)_target {
269 [self write:@"<![CDATA[" to:_target];
270 [self outputNodeList:[_node childNodes] to:_target];
271 [self write:@"]]>" to:_target];
274 - (void)outputPI:(id<DOMProcessingInstruction>)_node to:(id)_target {
275 [self indentOn:_target];
276 [self write:@"<?" to:_target];
277 [self write:[_node target] to:_target];
278 [self write:@" " to:_target];
279 [self write:[_node data] to:_target];
280 [self write:@"?>\n" to:_target];
283 - (void)outputNode:(id<DOMNode>)_node to:(id)_target {
284 switch ([_node nodeType]) {
285 case DOM_ELEMENT_NODE:
286 [self outputElementNode:(id)_node to:_target];
288 case DOM_CDATA_SECTION_NODE:
289 [self outputCDATA:(id)_node to:_target];
291 case DOM_PROCESSING_INSTRUCTION_NODE:
292 [self outputPI:(id)_node to:_target];
295 [self outputTextNode:(id)_node to:_target];
297 case DOM_COMMENT_NODE:
298 [self outputCommentNode:(id)_node to:_target];
302 NSLog(@"cannot output node '%@'", _node);
306 - (void)outputNodeList:(id<DOMNodeList>)_nodeList to:(id)_target {
310 children = _nodeList;
312 for (i = 0, count = [children count]; i < count; i++)
313 [self outputNode:[children objectAtIndex:i] to:_target];
316 - (void)outputDocument:(id)_document to:(id)_target {
317 if (![_document hasChildNodes]) {
318 NSLog(@"ERROR: document has no childnodes !");
322 [self write:@"<?xml version=\"1.0\"?>\n" to:_target];
324 [self->stack removeAllObjects];
325 [self outputNodeList:[_document childNodes] to:_target];
336 @end /* DOMXMLOutputter */
339 @implementation DOMXMLOutputter(PrefixStack)
341 - (void)_checkPrefixStack {
342 NSAssert2(([self->stack count] % 2 == 0),
343 @"%s: prefixStack is not valid (%@)!!!",
348 - (NSString *)topPrefix {
349 [self _checkPrefixStack];
350 if ([self->stack count] == 0) return nil;
351 return [self->stack objectAtIndex:[self->stack count] -2];
354 - (NSString *)topNamespace {
355 [self _checkPrefixStack];
356 if ([self->stack count] == 0) return nil;
357 return [self->stack lastObject];
360 - (void)pushPrefix:(NSString *)_prefix namespace:(NSString *)_namespace {
361 [self _checkPrefixStack];
362 [self->stack addObject:(_prefix) ? _prefix : @""];
363 [self->stack addObject:(_namespace) ? _namespace : @""];
366 - (void)popPrefixAndNamespace {
367 [self _checkPrefixStack];
368 NSAssert1(([self->stack count] > 0), @"%s: prefixStack.count == 0",
369 __PRETTY_FUNCTION__);
370 [self->stack removeLastObject]; // namespace
371 [self->stack removeLastObject]; // prefix
374 - (BOOL)isTagValidInStack:(id)_node {
375 NSString *nodeNamespace;
376 NSString *nodePrefix;
379 nodePrefix = [_node prefix];
380 nodeNamespace = [_node namespaceURI];
382 for (i = [self->stack count]; i >= 2; i = i - 2) {
386 prefix = [self->stack objectAtIndex:i-2];
387 namespace = [self->stack objectAtIndex:i-1];
388 if ([nodePrefix isEqualToString:prefix] &&
389 [nodeNamespace isEqualToString:namespace])
395 - (NSArray *)newAttributePrefixesAndNamespaces:(NSArray *)_attrs {
396 NSMutableArray *result;
399 count = [_attrs count];
401 if (count == 0) return [NSArray array];
403 result = [[NSMutableArray alloc] initWithCapacity:count];
404 for (j = 0; j < count; j++) {
406 NSString *attrNamespace;
407 NSString *attrPrefix;
410 attr = [_attrs objectAtIndex:j];
411 attrNamespace = [attr namespaceURI];
412 attrPrefix = [attr prefix];
413 attrNamespace = (attrNamespace) ? attrNamespace : @"";
414 attrPrefix = (attrPrefix) ? attrPrefix : @"";
416 if (([attrNamespace length] == 0 && [attrPrefix length] == 0)) continue;
418 for (i = [self->stack count]; i >= 2; i = i - 2) {
422 prefix = [self->stack objectAtIndex:i-2];
423 namespace = [self->stack objectAtIndex:i-1];
424 if ([attrPrefix isEqualToString:prefix] &&
425 [attrNamespace isEqualToString:namespace]) {
430 if (didMatch == NO) {
431 [result addObject:attrPrefix];
432 [result addObject:attrNamespace];
435 return [result autorelease];
438 @end /* DOMXMLOutputter(PrefixStack) */