55using System . Linq ;
66using System . Text ;
77using System . Xml ;
8+ using System . Xml . Linq ;
89using System . Xml . Schema ;
910using System . Xml . XPath ;
1011
@@ -387,9 +388,13 @@ public void SaveCopy(string filename)
387388
388389 var encoding = GetEncoding ( ) ;
389390 s . Encoding = encoding ;
391+
390392 bool noBom = false ;
393+ bool useNewWriter = true ;
391394 if ( this . _site != null )
392395 {
396+ EncodingHelpers . InitializeWriterSettings ( s , this . _site ) ;
397+
393398 Settings settings = ( Settings ) this . _site . GetService ( typeof ( Settings ) ) ;
394399 if ( settings != null )
395400 {
@@ -401,37 +406,183 @@ public void SaveCopy(string filename)
401406 }
402407 }
403408 }
404- if ( noBom )
409+
410+ MemoryStream ms = new MemoryStream ( ) ;
411+ if ( useNewWriter )
412+ {
413+ using ( var writer = XmlWriter . Create ( ms , s ) )
414+ {
415+ this . WriteTo ( writer ) ;
416+ }
417+ }
418+ else
405419 {
406- MemoryStream ms = new MemoryStream ( ) ;
407- // The new XmlWriter.Create method returns a writer that is too strict and does not
408- // allow xmlns attributes that override the parent element NamespaceURI. Using that
409- // writer would require very complex (and very slow) recreation of XML Element nodes
410- // in the tree (and therefore all their children also) every time and xmlns attribute
411- // is modified.
412420 using ( XmlTextWriter w = new XmlTextWriter ( ms , encoding ) )
413421 {
414422 EncodingHelpers . InitializeWriterSettings ( w , this . _site ) ;
415- _doc . Save ( w ) ;
423+ this . WriteTo ( w ) ;
416424 }
425+ }
417426
427+ if ( noBom )
428+ {
418429 using ( var stm = new MemoryStream ( ms . ToArray ( ) ) )
419430 {
420431 EncodingHelpers . WriteFileWithoutBOM ( stm , filename ) ;
421432 }
422433 }
423434 else
424435 {
425- using ( XmlTextWriter w = new XmlTextWriter ( filename , encoding ) )
436+ // doing the write this way ensures that an XML exception doesn't result in
437+ // wiping the previous state of the file on disk.
438+ File . WriteAllBytes ( filename , ms . ToArray ( ) ) ;
439+ }
440+ }
441+ finally
442+ {
443+ StartFileWatch ( ) ;
444+ }
445+ }
446+
447+ private void WriteTo ( XmlWriter w )
448+ {
449+ // The new XmlWriter.Create method returns a writer that is strict and does not
450+ // allow xmlns attributes that override the parent element NamespaceURI. Fixing that
451+ // in the XmlElement tree would require very complex (and very slow) recreation of
452+ // XML Element nodes in the tree (and therefore all their children also) every time an
453+ // xmlns attribute is modified. So we deal with that here instead during save by calling
454+ // the XmlWriter ourselves with the correct namespaces on the WriteStartElement call.
455+ XmlNode xmlNode = _doc . FirstChild ;
456+ if ( xmlNode == null )
457+ {
458+ return ;
459+ }
460+ if ( w . WriteState == WriteState . Start )
461+ {
462+ if ( xmlNode is XmlDeclaration )
463+ {
464+ if ( Standalone . Length == 0 )
426465 {
427- EncodingHelpers . InitializeWriterSettings ( w , this . _site ) ;
428- _doc . Save ( w ) ;
466+ w . WriteStartDocument ( ) ;
467+ }
468+ else if ( Standalone == "yes" )
469+ {
470+ w . WriteStartDocument ( standalone : true ) ;
429471 }
472+ else if ( Standalone == "no" )
473+ {
474+ w . WriteStartDocument ( standalone : false ) ;
475+ }
476+ xmlNode = xmlNode . NextSibling ;
477+ }
478+ else
479+ {
480+ w . WriteStartDocument ( ) ;
430481 }
431482 }
432- finally
483+ var scope = new XmlNamespaceManager ( _doc . NameTable ) ;
484+ while ( xmlNode != null )
433485 {
434- StartFileWatch ( ) ;
486+ WriteNode ( xmlNode , w , scope ) ;
487+ xmlNode = xmlNode . NextSibling ;
488+ }
489+ w . Flush ( ) ;
490+ }
491+
492+ internal void WriteNode ( XmlNode node , XmlWriter w , XmlNamespaceManager scope )
493+ {
494+ if ( node is XmlElement e )
495+ {
496+ WriteElementTo ( w , e , scope ) ;
497+ }
498+ else
499+ {
500+ node . WriteTo ( w ) ;
501+ }
502+ }
503+
504+ private void WriteElementTo ( XmlWriter writer , XmlElement e , XmlNamespaceManager scope )
505+ {
506+ XmlNode xmlNode = e ;
507+ XmlNode xmlNode2 = e ;
508+ while ( true )
509+ {
510+ e = xmlNode2 as XmlElement ;
511+ if ( e != null )
512+ {
513+ scope . PushScope ( ) ;
514+ for ( int i = 0 ; i < e . Attributes . Count ; i ++ )
515+ {
516+ XmlAttribute xmlAttribute = e . Attributes [ i ] ;
517+ if ( xmlAttribute . NamespaceURI == XmlStandardUris . XmlnsUri )
518+ {
519+ var prefix = xmlAttribute . Prefix == "xmlns" ? xmlAttribute . LocalName : "" ;
520+ scope . AddNamespace ( prefix , xmlAttribute . Value ) ;
521+ }
522+ }
523+
524+ WriteStartElement ( writer , e , scope ) ;
525+ if ( e . IsEmpty )
526+ {
527+ writer . WriteEndElement ( ) ;
528+ scope . PopScope ( ) ;
529+ }
530+ else
531+ {
532+ if ( e . LastChild != null )
533+ {
534+ xmlNode2 = e . FirstChild ;
535+ continue ;
536+ }
537+ writer . WriteFullEndElement ( ) ;
538+ scope . PopScope ( ) ;
539+ }
540+ }
541+ else
542+ {
543+ WriteNode ( xmlNode2 , writer , scope ) ;
544+ }
545+ while ( xmlNode2 != xmlNode && xmlNode2 == xmlNode2 . ParentNode . LastChild )
546+ {
547+ xmlNode2 = xmlNode2 . ParentNode ;
548+ writer . WriteFullEndElement ( ) ;
549+ scope . PopScope ( ) ;
550+ }
551+ if ( xmlNode2 != xmlNode )
552+ {
553+ xmlNode2 = xmlNode2 . NextSibling ;
554+ continue ;
555+ }
556+ break ;
557+ }
558+ }
559+
560+ private void WriteStartElement ( XmlWriter w , XmlElement e , XmlNamespaceManager scope )
561+ {
562+ // Fix up the element namespace so that the XmlWriter doesn't complain!
563+ var ns = scope . LookupNamespace ( e . Prefix ) ;
564+ w . WriteStartElement ( e . Prefix , e . LocalName , ns ) ;
565+ if ( e . HasAttributes )
566+ {
567+ XmlAttributeCollection xmlAttributeCollection = e . Attributes ;
568+ for ( int i = 0 ; i < xmlAttributeCollection . Count ; i ++ )
569+ {
570+ XmlAttribute xmlAttribute = xmlAttributeCollection [ i ] ;
571+ xmlAttribute . WriteTo ( w ) ;
572+ }
573+ }
574+ }
575+
576+
577+ public string Standalone
578+ {
579+ get
580+ {
581+ if ( this . _doc . FirstChild is XmlDeclaration x )
582+ {
583+ return x . Standalone ;
584+ }
585+ return "" ;
435586 }
436587 }
437588
0 commit comments