1 //------------------------------------------------------------------------------ 2 // <copyright file="XmlQueryStaticData.cs" company="Microsoft"> 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // </copyright> 5 // <owner current="true" primary="true">Microsoft</owner> 6 //------------------------------------------------------------------------------ 7 8 using System.Collections.Generic; 9 using System.Diagnostics; 10 using System.IO; 11 using System.Reflection; 12 using System.Xml.Xsl.IlGen; 13 using System.Xml.Xsl.Qil; 14 15 namespace System.Xml.Xsl.Runtime { 16 /// <summary> 17 /// Contains all static data that is used by the runtime. 18 /// </summary> 19 internal class XmlQueryStaticData { 20 // Name of the field to serialize to 21 public const string DataFieldName = "staticData"; 22 public const string TypesFieldName = "ebTypes"; 23 24 // Format version marker to support versioning: (major << 8) | minor 25 private const int CurrentFormatVersion = (0 << 8) | 0; 26 27 private XmlWriterSettings defaultWriterSettings; 28 private IList<WhitespaceRule> whitespaceRules; 29 private string[] names; 30 private StringPair[][] prefixMappingsList; 31 private Int32Pair[] filters; 32 private XmlQueryType[] types; 33 private XmlCollation[] collations; 34 private string[] globalNames; 35 private EarlyBoundInfo[] earlyBound; 36 37 /// <summary> 38 /// Constructor. 39 /// </summary> XmlQueryStaticData(XmlWriterSettings defaultWriterSettings, IList<WhitespaceRule> whitespaceRules, StaticDataManager staticData)40 public XmlQueryStaticData(XmlWriterSettings defaultWriterSettings, IList<WhitespaceRule> whitespaceRules, StaticDataManager staticData) { 41 Debug.Assert(defaultWriterSettings != null && staticData != null); 42 this.defaultWriterSettings = defaultWriterSettings; 43 this.whitespaceRules = whitespaceRules; 44 this.names = staticData.Names; 45 this.prefixMappingsList = staticData.PrefixMappingsList; 46 this.filters = staticData.NameFilters; 47 this.types = staticData.XmlTypes; 48 this.collations = staticData.Collations; 49 this.globalNames = staticData.GlobalNames; 50 this.earlyBound = staticData.EarlyBound; 51 52 #if DEBUG 53 // Round-trip check 54 byte[] data; 55 Type[] ebTypes; 56 this.GetObjectData(out data, out ebTypes); 57 XmlQueryStaticData copy = new XmlQueryStaticData(data, ebTypes); 58 59 this.defaultWriterSettings = copy.defaultWriterSettings; 60 this.whitespaceRules = copy.whitespaceRules; 61 this.names = copy.names; 62 this.prefixMappingsList = copy.prefixMappingsList; 63 this.filters = copy.filters; 64 this.types = copy.types; 65 this.collations = copy.collations; 66 this.globalNames = copy.globalNames; 67 this.earlyBound = copy.earlyBound; 68 #endif 69 } 70 71 /// <summary> 72 /// Deserialize XmlQueryStaticData object from a byte array. 73 /// </summary> XmlQueryStaticData(byte[] data, Type[] ebTypes)74 public XmlQueryStaticData(byte[] data, Type[] ebTypes) { 75 MemoryStream dataStream = new MemoryStream(data, /*writable:*/false); 76 XmlQueryDataReader dataReader = new XmlQueryDataReader(dataStream); 77 int length; 78 79 // Read a format version 80 int formatVersion = dataReader.ReadInt32Encoded(); 81 82 // Changes in the major part of version are not supported 83 if ((formatVersion & ~0xff) > CurrentFormatVersion) 84 throw new NotSupportedException(); 85 86 // XmlWriterSettings defaultWriterSettings; 87 defaultWriterSettings = new XmlWriterSettings(dataReader); 88 89 // IList<WhitespaceRule> whitespaceRules; 90 length = dataReader.ReadInt32(); 91 if (length != 0) { 92 this.whitespaceRules = new WhitespaceRule[length]; 93 for (int idx = 0; idx < length; idx++) { 94 this.whitespaceRules[idx] = new WhitespaceRule(dataReader); 95 } 96 } 97 98 // string[] names; 99 length = dataReader.ReadInt32(); 100 if (length != 0) { 101 this.names = new string[length]; 102 for (int idx = 0; idx < length; idx++) { 103 this.names[idx] = dataReader.ReadString(); 104 } 105 } 106 107 // StringPair[][] prefixMappingsList; 108 length = dataReader.ReadInt32(); 109 if (length != 0) { 110 this.prefixMappingsList = new StringPair[length][]; 111 for (int idx = 0; idx < length; idx++) { 112 int length2 = dataReader.ReadInt32(); 113 this.prefixMappingsList[idx] = new StringPair[length2]; 114 for (int idx2 = 0; idx2 < length2; idx2++) { 115 this.prefixMappingsList[idx][idx2] = new StringPair(dataReader.ReadString(), dataReader.ReadString()); 116 } 117 } 118 } 119 120 // Int32Pair[] filters; 121 length = dataReader.ReadInt32(); 122 if (length != 0) { 123 this.filters = new Int32Pair[length]; 124 for (int idx = 0; idx < length; idx++) { 125 this.filters[idx] = new Int32Pair(dataReader.ReadInt32Encoded(), dataReader.ReadInt32Encoded()); 126 } 127 } 128 129 // XmlQueryType[] types; 130 length = dataReader.ReadInt32(); 131 if (length != 0) { 132 this.types = new XmlQueryType[length]; 133 for (int idx = 0; idx < length; idx++) { 134 this.types[idx] = XmlQueryTypeFactory.Deserialize(dataReader); 135 } 136 } 137 138 // XmlCollation[] collations; 139 length = dataReader.ReadInt32(); 140 if (length != 0) { 141 this.collations = new XmlCollation[length]; 142 for (int idx = 0; idx < length; idx++) { 143 this.collations[idx] = new XmlCollation(dataReader); 144 } 145 } 146 147 // string[] globalNames; 148 length = dataReader.ReadInt32(); 149 if (length != 0) { 150 this.globalNames = new string[length]; 151 for (int idx = 0; idx < length; idx++) { 152 this.globalNames[idx] = dataReader.ReadString(); 153 } 154 } 155 156 // EarlyBoundInfo[] earlyBound; 157 length = dataReader.ReadInt32(); 158 if (length != 0) { 159 this.earlyBound = new EarlyBoundInfo[length]; 160 for (int idx = 0; idx < length; idx++) { 161 this.earlyBound[idx] = new EarlyBoundInfo(dataReader.ReadString(), ebTypes[idx]); 162 } 163 } 164 165 Debug.Assert(formatVersion != CurrentFormatVersion || dataReader.Read() == -1, "Extra data at the end of the stream"); 166 dataReader.Close(); 167 } 168 169 /// <summary> 170 /// Serialize XmlQueryStaticData object into a byte array. 171 /// </summary> GetObjectData(out byte[] data, out Type[] ebTypes)172 public void GetObjectData(out byte[] data, out Type[] ebTypes) { 173 MemoryStream dataStream = new MemoryStream(4096); 174 XmlQueryDataWriter dataWriter = new XmlQueryDataWriter(dataStream); 175 176 // First put the format version 177 dataWriter.WriteInt32Encoded(CurrentFormatVersion); 178 179 // XmlWriterSettings defaultWriterSettings; 180 defaultWriterSettings.GetObjectData(dataWriter); 181 182 // IList<WhitespaceRule> whitespaceRules; 183 if (this.whitespaceRules == null) { 184 dataWriter.Write(0); 185 } 186 else { 187 dataWriter.Write(this.whitespaceRules.Count); 188 foreach (WhitespaceRule rule in this.whitespaceRules) { 189 rule.GetObjectData(dataWriter); 190 } 191 } 192 193 // string[] names; 194 if (this.names == null) { 195 dataWriter.Write(0); 196 } 197 else { 198 dataWriter.Write(this.names.Length); 199 foreach (string name in this.names) { 200 dataWriter.Write(name); 201 } 202 } 203 204 // StringPair[][] prefixMappingsList; 205 if (this.prefixMappingsList == null) { 206 dataWriter.Write(0); 207 } 208 else { 209 dataWriter.Write(this.prefixMappingsList.Length); 210 foreach (StringPair[] mappings in this.prefixMappingsList) { 211 dataWriter.Write(mappings.Length); 212 foreach (StringPair mapping in mappings) { 213 dataWriter.Write(mapping.Left); 214 dataWriter.Write(mapping.Right); 215 } 216 } 217 } 218 219 // Int32Pair[] filters; 220 if (this.filters == null) { 221 dataWriter.Write(0); 222 } 223 else { 224 dataWriter.Write(this.filters.Length); 225 foreach (Int32Pair filter in this.filters) { 226 dataWriter.WriteInt32Encoded(filter.Left); 227 dataWriter.WriteInt32Encoded(filter.Right); 228 } 229 } 230 231 // XmlQueryType[] types; 232 if (this.types == null) { 233 dataWriter.Write(0); 234 } 235 else { 236 dataWriter.Write(this.types.Length); 237 foreach (XmlQueryType type in this.types) { 238 XmlQueryTypeFactory.Serialize(dataWriter, type); 239 } 240 } 241 242 // XmlCollation[] collations; 243 if (collations == null) { 244 dataWriter.Write(0); 245 } 246 else { 247 dataWriter.Write(this.collations.Length); 248 foreach (XmlCollation collation in this.collations) { 249 collation.GetObjectData(dataWriter); 250 } 251 } 252 253 // string[] globalNames; 254 if (this.globalNames == null) { 255 dataWriter.Write(0); 256 } 257 else { 258 dataWriter.Write(this.globalNames.Length); 259 foreach (string name in this.globalNames) { 260 dataWriter.Write(name); 261 } 262 } 263 264 // EarlyBoundInfo[] earlyBound; 265 if (this.earlyBound == null) { 266 dataWriter.Write(0); 267 ebTypes = null; 268 } 269 else { 270 dataWriter.Write(this.earlyBound.Length); 271 ebTypes = new Type[this.earlyBound.Length]; 272 int idx = 0; 273 foreach (EarlyBoundInfo info in this.earlyBound) { 274 dataWriter.Write(info.NamespaceUri); 275 ebTypes[idx++] = info.EarlyBoundType; 276 } 277 } 278 279 dataWriter.Close(); 280 data = dataStream.ToArray(); 281 } 282 283 /// <summary> 284 /// Return the default writer settings. 285 /// </summary> 286 public XmlWriterSettings DefaultWriterSettings { 287 get { return this.defaultWriterSettings; } 288 } 289 290 /// <summary> 291 /// Return the rules used for whitespace stripping/preservation. 292 /// </summary> 293 public IList<WhitespaceRule> WhitespaceRules { 294 get { return this.whitespaceRules; } 295 } 296 297 /// <summary> 298 /// Return array of names used by this query. 299 /// </summary> 300 public string[] Names { 301 get { return this.names; } 302 } 303 304 /// <summary> 305 /// Return array of prefix mappings used by this query. 306 /// </summary> 307 public StringPair[][] PrefixMappingsList { 308 get { return this.prefixMappingsList; } 309 } 310 311 /// <summary> 312 /// Return array of name filter specifications used by this query. 313 /// </summary> 314 public Int32Pair[] Filters { 315 get { return this.filters; } 316 } 317 318 /// <summary> 319 /// Return array of types used by this query. 320 /// </summary> 321 public XmlQueryType[] Types { 322 get { return this.types; } 323 } 324 325 /// <summary> 326 /// Return array of collations used by this query. 327 /// </summary> 328 public XmlCollation[] Collations { 329 get { return this.collations; } 330 } 331 332 /// <summary> 333 /// Return names of all global variables and parameters used by this query. 334 /// </summary> 335 public string[] GlobalNames { 336 get { return this.globalNames; } 337 } 338 339 /// <summary> 340 /// Return array of early bound object information related to this query. 341 /// </summary> 342 public EarlyBoundInfo[] EarlyBound { 343 get { return this.earlyBound; } 344 } 345 } 346 347 /// <summary> 348 /// Subclass of BinaryReader used to serialize query static data. 349 /// </summary> 350 internal class XmlQueryDataReader : BinaryReader { XmlQueryDataReader(Stream input)351 public XmlQueryDataReader(Stream input) : base(input) { } 352 353 /// <summary> 354 /// Read in a 32-bit integer in compressed format. 355 /// </summary> ReadInt32Encoded()356 public int ReadInt32Encoded() { 357 return Read7BitEncodedInt(); 358 } 359 360 /// <summary> 361 /// Read a string value from the stream. Value can be null. 362 /// </summary> ReadStringQ()363 public string ReadStringQ() { 364 return ReadBoolean() ? ReadString() : null; 365 } 366 367 /// <summary> 368 /// Read a signed byte value from the stream and check if it belongs to the given diapason. 369 /// </summary> ReadSByte(sbyte minValue, sbyte maxValue)370 public sbyte ReadSByte(sbyte minValue, sbyte maxValue) { 371 sbyte value = ReadSByte(); 372 if (value < minValue) 373 throw new ArgumentOutOfRangeException("minValue"); 374 if (maxValue < value) 375 throw new ArgumentOutOfRangeException("maxValue"); 376 377 return value; 378 } 379 } 380 381 /// <summary> 382 /// Subclass of BinaryWriter used to deserialize query static data. 383 /// </summary> 384 internal class XmlQueryDataWriter : BinaryWriter { XmlQueryDataWriter(Stream output)385 public XmlQueryDataWriter(Stream output) : base(output) { } 386 387 /// <summary> 388 /// Write a 32-bit integer in a compressed format. 389 /// </summary> WriteInt32Encoded(int value)390 public void WriteInt32Encoded(int value) { 391 Write7BitEncodedInt(value); 392 } 393 394 /// <summary> 395 /// Write a string value to the stream. Value can be null. 396 /// </summary> WriteStringQ(string value)397 public void WriteStringQ(string value) { 398 Write(value != null); 399 if (value != null) { 400 Write(value); 401 } 402 } 403 } 404 } 405