1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System.IO; 6 using System.Runtime; 7 using System.Runtime.Serialization; 8 using System.Xml; 9 using System.Xml.Serialization; 10 using System.Runtime.CompilerServices; 11 using System.Diagnostics; 12 13 namespace System.ServiceModel.Syndication 14 { 15 public class SyndicationElementExtension 16 { 17 private XmlBuffer _buffer; 18 private int _bufferElementIndex; 19 // extensionData and extensionDataWriter are only present on the send side 20 private object _extensionData; 21 private ExtensionDataWriter _extensionDataWriter; 22 private string _outerName; 23 private string _outerNamespace; 24 SyndicationElementExtension(XmlReader xmlReader)25 public SyndicationElementExtension(XmlReader xmlReader) 26 { 27 if (xmlReader == null) 28 { 29 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("xmlReader"); 30 } 31 SyndicationFeedFormatter.MoveToStartElement(xmlReader); 32 _outerName = xmlReader.LocalName; 33 _outerNamespace = xmlReader.NamespaceURI; 34 _buffer = new XmlBuffer(int.MaxValue); 35 using (XmlDictionaryWriter writer = _buffer.OpenSection(XmlDictionaryReaderQuotas.Max)) 36 { 37 writer.WriteStartElement(Rss20Constants.ExtensionWrapperTag); 38 writer.WriteNode(xmlReader, false); 39 writer.WriteEndElement(); 40 } 41 _buffer.CloseSection(); 42 _buffer.Close(); 43 _bufferElementIndex = 0; 44 } 45 SyndicationElementExtension(object dataContractExtension)46 public SyndicationElementExtension(object dataContractExtension) 47 : this(dataContractExtension, (XmlObjectSerializer)null) 48 { 49 } 50 SyndicationElementExtension(object dataContractExtension, XmlObjectSerializer dataContractSerializer)51 public SyndicationElementExtension(object dataContractExtension, XmlObjectSerializer dataContractSerializer) 52 : this(null, null, dataContractExtension, dataContractSerializer) 53 { 54 } 55 SyndicationElementExtension(string outerName, string outerNamespace, object dataContractExtension)56 public SyndicationElementExtension(string outerName, string outerNamespace, object dataContractExtension) 57 : this(outerName, outerNamespace, dataContractExtension, null) 58 { 59 } 60 SyndicationElementExtension(string outerName, string outerNamespace, object dataContractExtension, XmlObjectSerializer dataContractSerializer)61 public SyndicationElementExtension(string outerName, string outerNamespace, object dataContractExtension, XmlObjectSerializer dataContractSerializer) 62 { 63 if (dataContractExtension == null) 64 { 65 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("dataContractExtension"); 66 } 67 if (outerName == string.Empty) 68 { 69 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.Format(SR.OuterNameOfElementExtensionEmpty)); 70 } 71 if (dataContractSerializer == null) 72 { 73 dataContractSerializer = new DataContractSerializer(dataContractExtension.GetType()); 74 } 75 _outerName = outerName; 76 _outerNamespace = outerNamespace; 77 _extensionData = dataContractExtension; 78 _extensionDataWriter = new ExtensionDataWriter(_extensionData, dataContractSerializer, _outerName, _outerNamespace); 79 } 80 SyndicationElementExtension(object xmlSerializerExtension, XmlSerializer serializer)81 public SyndicationElementExtension(object xmlSerializerExtension, XmlSerializer serializer) 82 { 83 if (xmlSerializerExtension == null) 84 { 85 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("xmlSerializerExtension"); 86 } 87 if (serializer == null) 88 { 89 serializer = new XmlSerializer(xmlSerializerExtension.GetType()); 90 } 91 _extensionData = xmlSerializerExtension; 92 _extensionDataWriter = new ExtensionDataWriter(_extensionData, serializer); 93 } 94 SyndicationElementExtension(XmlBuffer buffer, int bufferElementIndex, string outerName, string outerNamespace)95 internal SyndicationElementExtension(XmlBuffer buffer, int bufferElementIndex, string outerName, string outerNamespace) 96 { 97 _buffer = buffer; 98 _bufferElementIndex = bufferElementIndex; 99 _outerName = outerName; 100 _outerNamespace = outerNamespace; 101 } 102 103 public string OuterName 104 { 105 get 106 { 107 if (_outerName == null) 108 { 109 EnsureOuterNameAndNs(); 110 } 111 return _outerName; 112 } 113 } 114 115 public string OuterNamespace 116 { 117 get 118 { 119 if (_outerName == null) 120 { 121 EnsureOuterNameAndNs(); 122 } 123 return _outerNamespace; 124 } 125 } 126 GetObject()127 public TExtension GetObject<TExtension>() 128 { 129 return GetObject<TExtension>(new DataContractSerializer(typeof(TExtension))); 130 } 131 GetObject(XmlObjectSerializer serializer)132 public TExtension GetObject<TExtension>(XmlObjectSerializer serializer) 133 { 134 if (serializer == null) 135 { 136 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("serializer"); 137 } 138 if (_extensionData != null && typeof(TExtension).IsAssignableFrom(_extensionData.GetType())) 139 { 140 return (TExtension)_extensionData; 141 } 142 using (XmlReader reader = GetReader()) 143 { 144 return (TExtension)serializer.ReadObject(reader, false); 145 } 146 } 147 GetObject(XmlSerializer serializer)148 public TExtension GetObject<TExtension>(XmlSerializer serializer) 149 { 150 if (serializer == null) 151 { 152 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("serializer"); 153 } 154 if (_extensionData != null && typeof(TExtension).IsAssignableFrom(_extensionData.GetType())) 155 { 156 return (TExtension)_extensionData; 157 } 158 using (XmlReader reader = GetReader()) 159 { 160 return (TExtension)serializer.Deserialize(reader); 161 } 162 } 163 GetReader()164 public XmlReader GetReader() 165 { 166 this.EnsureBuffer(); 167 XmlReader reader = _buffer.GetReader(0); 168 int index = 0; 169 reader.ReadStartElement(Rss20Constants.ExtensionWrapperTag); 170 while (reader.IsStartElement()) 171 { 172 if (index == _bufferElementIndex) 173 { 174 break; 175 } 176 ++index; 177 reader.Skip(); 178 } 179 return reader; 180 } 181 WriteTo(XmlWriter writer)182 public void WriteTo(XmlWriter writer) 183 { 184 if (writer == null) 185 { 186 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer"); 187 } 188 if (_extensionDataWriter != null) 189 { 190 _extensionDataWriter.WriteTo(writer); 191 } 192 else 193 { 194 using (XmlReader reader = GetReader()) 195 { 196 writer.WriteNode(reader, false); 197 } 198 } 199 } 200 EnsureBuffer()201 private void EnsureBuffer() 202 { 203 if (_buffer == null) 204 { 205 _buffer = new XmlBuffer(int.MaxValue); 206 using (XmlDictionaryWriter writer = _buffer.OpenSection(XmlDictionaryReaderQuotas.Max)) 207 { 208 writer.WriteStartElement(Rss20Constants.ExtensionWrapperTag); 209 this.WriteTo(writer); 210 writer.WriteEndElement(); 211 } 212 _buffer.CloseSection(); 213 _buffer.Close(); 214 _bufferElementIndex = 0; 215 } 216 } 217 EnsureOuterNameAndNs()218 private void EnsureOuterNameAndNs() 219 { 220 Debug.Assert(_extensionDataWriter != null, "outer name is null only for datacontract and xmlserializer cases"); 221 _extensionDataWriter.ComputeOuterNameAndNs(out _outerName, out _outerNamespace); 222 } 223 224 // this class holds the extension data and the associated serializer (either DataContractSerializer or XmlSerializer but not both) 225 private class ExtensionDataWriter 226 { 227 private readonly XmlObjectSerializer _dataContractSerializer; 228 private readonly object _extensionData; 229 private readonly string _outerName; 230 private readonly string _outerNamespace; 231 private readonly XmlSerializer _xmlSerializer; 232 ExtensionDataWriter(object extensionData, XmlObjectSerializer dataContractSerializer, string outerName, string outerNamespace)233 public ExtensionDataWriter(object extensionData, XmlObjectSerializer dataContractSerializer, string outerName, string outerNamespace) 234 { 235 Debug.Assert(extensionData != null && dataContractSerializer != null, "null check"); 236 _dataContractSerializer = dataContractSerializer; 237 _extensionData = extensionData; 238 _outerName = outerName; 239 _outerNamespace = outerNamespace; 240 } 241 ExtensionDataWriter(object extensionData, XmlSerializer serializer)242 public ExtensionDataWriter(object extensionData, XmlSerializer serializer) 243 { 244 Debug.Assert(extensionData != null && serializer != null, "null check"); 245 _xmlSerializer = serializer; 246 _extensionData = extensionData; 247 } 248 WriteTo(XmlWriter writer)249 public void WriteTo(XmlWriter writer) 250 { 251 if (_xmlSerializer != null) 252 { 253 Debug.Assert((_dataContractSerializer == null && _outerName == null && _outerNamespace == null), "Xml serializer cannot have outer name, ns"); 254 _xmlSerializer.Serialize(writer, _extensionData); 255 } 256 else 257 { 258 Debug.Assert(_xmlSerializer == null, "Xml serializer cannot be configured"); 259 if (_outerName != null) 260 { 261 writer.WriteStartElement(_outerName, _outerNamespace); 262 _dataContractSerializer.WriteObjectContent(writer, _extensionData); 263 writer.WriteEndElement(); 264 } 265 else 266 { 267 _dataContractSerializer.WriteObject(writer, _extensionData); 268 } 269 } 270 } 271 ComputeOuterNameAndNs(out string name, out string ns)272 internal void ComputeOuterNameAndNs(out string name, out string ns) 273 { 274 if (_outerName != null) 275 { 276 Debug.Assert(_xmlSerializer == null, "outer name is not null for data contract extension only"); 277 name = _outerName; 278 ns = _outerNamespace; 279 } 280 else if (_dataContractSerializer != null) 281 { 282 Debug.Assert(_xmlSerializer == null, "only one of xmlserializer or datacontract serializer can be present"); 283 XsdDataContractExporter dcExporter = new XsdDataContractExporter(); 284 XmlQualifiedName qName = dcExporter.GetRootElementName(_extensionData.GetType()); 285 if (qName != null) 286 { 287 name = qName.Name; 288 ns = qName.Namespace; 289 } 290 else 291 { 292 // this can happen if an IXmlSerializable type is specified with IsAny=true 293 ReadOuterNameAndNs(out name, out ns); 294 } 295 } 296 else 297 { 298 Debug.Assert(_dataContractSerializer == null, "only one of xmlserializer or datacontract serializer can be present"); 299 XmlReflectionImporter importer = new XmlReflectionImporter(); 300 XmlTypeMapping typeMapping = importer.ImportTypeMapping(_extensionData.GetType()); 301 if (typeMapping != null && !string.IsNullOrEmpty(typeMapping.ElementName)) 302 { 303 name = typeMapping.ElementName; 304 ns = typeMapping.Namespace; 305 } 306 else 307 { 308 // this can happen if an IXmlSerializable type is specified with IsAny=true 309 ReadOuterNameAndNs(out name, out ns); 310 } 311 } 312 } 313 ReadOuterNameAndNs(out string name, out string ns)314 internal void ReadOuterNameAndNs(out string name, out string ns) 315 { 316 using (MemoryStream stream = new MemoryStream()) 317 { 318 using (XmlWriter writer = XmlWriter.Create(stream)) 319 { 320 this.WriteTo(writer); 321 } 322 stream.Seek(0, SeekOrigin.Begin); 323 using (XmlReader reader = XmlReader.Create(stream)) 324 { 325 SyndicationFeedFormatter.MoveToStartElement(reader); 326 name = reader.LocalName; 327 ns = reader.NamespaceURI; 328 } 329 } 330 } 331 } 332 } 333 } 334