1 //------------------------------------------------------------------------------
2 // <copyright file="XmlPreloadedResolver.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">[....]</owner>
6 //------------------------------------------------------------------------------
14 using System.Xml.Utils;
15 using System.Reflection;
16 using System.Diagnostics;
17 using System.Collections.Generic;
18 using System.Runtime.Versioning;
20 namespace System.Xml.Resolvers {
23 // XmlPreloadedResolver is an XmlResolver that which can be pre-loaded with data.
24 // By default it contains well-known DTDs for XHTML 1.0 and RSS 0.91.
25 // Custom mappings of URIs to data can be added with the Add method.
27 public partial class XmlPreloadedResolver : XmlResolver {
29 // PreloadedData class
31 abstract class PreloadedData {
32 // Returns preloaded data as Stream; Stream must always be supported
33 internal abstract Stream AsStream();
35 // Returns preloaded data as TextReader, or throws when not supported
36 internal virtual TextReader AsTextReader() {
37 throw new XmlException(Res.GetString(Res.Xml_UnsupportedClass));
40 // Returns true for types that are supported for this preloaded data; Stream must always be supported
41 internal virtual bool SupportsType(Type type) {
42 if (type == null || type == typeof(Stream)) {
50 // XmlKnownDtdData class
52 class XmlKnownDtdData : PreloadedData {
53 internal string publicId;
54 internal string systemId;
55 private string resourceName;
57 internal XmlKnownDtdData(string publicId, string systemId, string resourceName) {
58 this.publicId = publicId;
59 this.systemId = systemId;
60 this.resourceName = resourceName;
63 internal override Stream AsStream() {
64 Assembly asm = Assembly.GetExecutingAssembly();
65 return asm.GetManifestResourceStream(resourceName);
69 class ByteArrayChunk : PreloadedData {
74 internal ByteArrayChunk(byte[] array)
75 : this(array, 0, array.Length) {
78 internal ByteArrayChunk(byte[] array, int offset, int length) {
84 internal override Stream AsStream() {
85 return new MemoryStream(array, offset, length);
89 class StringData : PreloadedData {
92 internal StringData(string str) {
96 internal override Stream AsStream() {
97 return new MemoryStream(Encoding.Unicode.GetBytes(str));
100 internal override TextReader AsTextReader() {
101 return new StringReader(str);
104 internal override bool SupportsType(Type type) {
105 if (type == typeof(TextReader)) {
108 return base.SupportsType(type);
115 XmlResolver fallbackResolver;
116 Dictionary<Uri, PreloadedData> mappings;
117 XmlKnownDtds preloadedDtds;
120 // Static/constant fiels
122 static XmlKnownDtdData[] Xhtml10_Dtd = new XmlKnownDtdData[] {
123 new XmlKnownDtdData( "-//W3C//DTD XHTML 1.0 Strict//EN", "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd", "xhtml1-strict.dtd" ),
124 new XmlKnownDtdData( "-//W3C//DTD XHTML 1.0 Transitional//EN", "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd", "xhtml1-transitional.dtd" ),
125 new XmlKnownDtdData( "-//W3C//DTD XHTML 1.0 Frameset//EN", "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd", "xhtml1-frameset.dtd" ),
126 new XmlKnownDtdData( "-//W3C//ENTITIES Latin 1 for XHTML//EN", "http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent", "xhtml-lat1.ent" ),
127 new XmlKnownDtdData( "-//W3C//ENTITIES Symbols for XHTML//EN", "http://www.w3.org/TR/xhtml1/DTD/xhtml-symbol.ent", "xhtml-symbol.ent" ),
128 new XmlKnownDtdData( "-//W3C//ENTITIES Special for XHTML//EN", "http://www.w3.org/TR/xhtml1/DTD/xhtml-special.ent", "xhtml-special.ent" ),
131 static XmlKnownDtdData[] Rss091_Dtd = new XmlKnownDtdData[] {
132 new XmlKnownDtdData( "-//Netscape Communications//DTD RSS 0.91//EN", "http://my.netscape.com/publish/formats/rss-0.91.dtd", "rss-0.91.dtd" ),
138 public XmlPreloadedResolver()
142 public XmlPreloadedResolver(XmlKnownDtds preloadedDtds)
143 : this(null, preloadedDtds, null) {
146 public XmlPreloadedResolver(XmlResolver fallbackResolver)
147 : this(fallbackResolver, XmlKnownDtds.All, null) {
150 public XmlPreloadedResolver(XmlResolver fallbackResolver, XmlKnownDtds preloadedDtds)
151 : this (fallbackResolver, preloadedDtds, null) {
154 public XmlPreloadedResolver(XmlResolver fallbackResolver, XmlKnownDtds preloadedDtds, IEqualityComparer<Uri> uriComparer) {
155 this.fallbackResolver = fallbackResolver;
156 this.mappings = new Dictionary<Uri, PreloadedData>(16, uriComparer);
157 this.preloadedDtds = preloadedDtds;
160 if (preloadedDtds != 0) {
161 if ((preloadedDtds & XmlKnownDtds.Xhtml10) != 0) {
162 AddKnownDtd(Xhtml10_Dtd);
164 if ((preloadedDtds & XmlKnownDtds.Rss091) != 0) {
165 AddKnownDtd(Rss091_Dtd);
171 [ResourceExposure(ResourceScope.Machine)]
172 [ResourceConsumption(ResourceScope.Machine)]
174 public override Uri ResolveUri(Uri baseUri, string relativeUri) {
175 // 1) special-case well-known public IDs
176 // 2) To make FxCop happy we need to use StartsWith() overload that takes StringComparison ->
177 // .StartsWith(string) is equal to .StartsWith(string, StringComparison.CurrentCulture);
178 if (relativeUri != null && relativeUri.StartsWith("-//", StringComparison.CurrentCulture)) {
179 // 1) XHTML 1.0 public IDs
180 // 2) To make FxCop happy we need to use StartsWith() overload that takes StringComparison ->
181 // .StartsWith(string) is equal to .StartsWith(string, StringComparison.CurrentCulture);
182 if ((preloadedDtds & XmlKnownDtds.Xhtml10) != 0 && relativeUri.StartsWith("-//W3C//", StringComparison.CurrentCulture)) {
183 for (int i = 0; i < Xhtml10_Dtd.Length; i++) {
184 if (relativeUri == Xhtml10_Dtd[i].publicId) {
185 return new Uri(relativeUri, UriKind.Relative);
189 // RSS 0.91 public IDs
190 if ((preloadedDtds & XmlKnownDtds.Rss091) != 0) {
191 Debug.Assert(Rss091_Dtd.Length == 1);
192 if (relativeUri == Rss091_Dtd[0].publicId) {
193 return new Uri(relativeUri, UriKind.Relative);
197 return base.ResolveUri(baseUri, relativeUri);
200 public override Object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn) {
201 if (absoluteUri == null) {
202 throw new ArgumentNullException("absoluteUri");
206 if (!mappings.TryGetValue(absoluteUri, out data)) {
207 if (fallbackResolver != null) {
208 return fallbackResolver.GetEntity(absoluteUri, role, ofObjectToReturn);
210 throw new XmlException(Res.GetString(Res.Xml_CannotResolveUrl, absoluteUri.ToString()));
213 if (ofObjectToReturn == null || ofObjectToReturn == typeof(Stream) || ofObjectToReturn == typeof(Object)) {
214 return data.AsStream();
216 else if (ofObjectToReturn == typeof(TextReader)) {
217 return data.AsTextReader();
220 throw new XmlException(Res.GetString(Res.Xml_UnsupportedClass));
225 public override ICredentials Credentials {
227 if (fallbackResolver != null) {
228 fallbackResolver.Credentials = value;
234 public override bool SupportsType(Uri absoluteUri, Type type) {
235 if (absoluteUri == null) {
236 throw new ArgumentNullException("absoluteUri");
240 if (!mappings.TryGetValue(absoluteUri, out data)) {
241 if (fallbackResolver != null) {
242 return fallbackResolver.SupportsType(absoluteUri, type);
244 return base.SupportsType(absoluteUri, type);
247 return data.SupportsType(type);
250 public void Add(Uri uri, byte[] value) {
252 throw new ArgumentNullException("uri");
255 throw new ArgumentNullException("value");
257 Add(uri, new ByteArrayChunk(value, 0, value.Length));
260 public void Add(Uri uri, byte[] value, int offset, int count) {
262 throw new ArgumentNullException("uri");
265 throw new ArgumentNullException("value");
268 throw new ArgumentOutOfRangeException("count");
271 throw new ArgumentOutOfRangeException("offset");
273 if (value.Length - offset < count) {
274 throw new ArgumentOutOfRangeException("count");
277 Add(uri, new ByteArrayChunk(value, offset, count));
280 public void Add(Uri uri, Stream value) {
282 throw new ArgumentNullException("uri");
285 throw new ArgumentNullException("value");
288 // stream of known length -> allocate the byte array and read all data into it
289 int size = checked((int)value.Length);
290 byte[] bytes = new byte[size];
291 value.Read(bytes, 0, size);
292 Add(uri, new ByteArrayChunk(bytes));
295 // stream of unknown length -> read into memory stream and then get internal the byte array
296 MemoryStream ms = new MemoryStream();
297 byte[] buffer = new byte[4096];
299 while ((read = value.Read(buffer, 0, buffer.Length)) > 0) {
300 ms.Write(buffer, 0, read);
302 int size = checked((int)ms.Position);
303 byte[] bytes = new byte[size];
304 Array.Copy(ms.GetBuffer(), bytes, size);
305 Add(uri, new ByteArrayChunk(bytes));
309 public void Add(Uri uri, string value) {
311 throw new ArgumentNullException("uri");
314 throw new ArgumentNullException("value");
316 Add(uri, new StringData(value));
319 public IEnumerable<Uri> PreloadedUris {
321 // read-only collection of keys
322 return mappings.Keys;
326 public void Remove(Uri uri) {
328 throw new ArgumentNullException("uri");
330 mappings.Remove(uri);
334 // Private implementation methods
336 private void Add(Uri uri, PreloadedData data) {
337 Debug.Assert(uri != null);
339 // override if exists
340 if (mappings.ContainsKey(uri)) {
341 mappings[uri] = data;
344 mappings.Add(uri, data);
348 private void AddKnownDtd(XmlKnownDtdData[] dtdSet) {
349 for (int i = 0; i < dtdSet.Length; i++) {
350 XmlKnownDtdData dtdInfo = dtdSet[i];
351 mappings.Add(new Uri(dtdInfo.publicId, UriKind.RelativeOrAbsolute), dtdInfo);
352 mappings.Add(new Uri(dtdInfo.systemId, UriKind.RelativeOrAbsolute), dtdInfo);