Jypeli  9
The simple game programming library
StorageFile.cs
Siirry tämän tiedoston dokumentaatioon.
1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4 using System.IO;
5 using System.Xml.Serialization;
6 using System.Reflection;
7 using System.Xml;
8 using System.Collections;
9 using System.Globalization;
10 
11 namespace Jypeli
12 {
16  public class StorageFile : IDisposable
17  {
18  public string Name { get; set; }
19  public Stream Stream { get; private set; }
20 
21  internal StorageFile( string name, Stream stream )
22  {
23  Name = name;
24  Stream = stream;
25  if ( Stream == null ) Stream = new MemoryStream();
26  }
27 
28  public void Close()
29  {
30  Stream.Close();
31  }
32 
33  public void Dispose()
34  {
35  Stream.Close();
36  }
37 
38  internal static BindingFlags AllOfInstance = BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;
39 
40  string GenToString( Type type, object obj )
41  {
42  if ( type == typeof( string ) )
43  return (string)obj;
44 
45  IFormatProvider provider = NumberFormatInfo.InvariantInfo;
46 
47  try
48  {
49  object[] args = new object[] { provider };
50  return (string)type.InvokeMember( "ToString", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance, null, obj, args );
51  }
52  catch ( MissingMethodException )
53  {
54  object[] args = new object[] { };
55  return (string)type.InvokeMember( "ToString", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance, null, obj, args );
56  }
57  }
58 
59  object GenParse( Type type, string str )
60  {
61  if ( type == typeof( string ) )
62  return str;
63 
64  if ( type == typeof(Type) )
65  {
66  return TypeHelper.Parse( str );
67  }
68 
69  try
70  {
71  object[] args = new object[] { str, NumberFormatInfo.InvariantInfo };
72  return type.InvokeMember( "Parse", BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public, null, null, args );
73  }
74  catch ( MissingMethodException )
75  {
76  object[] args = new object[] { str };
77  return type.InvokeMember( "Parse", BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public, null, null, args );
78  }
79  }
80 
81  internal object LoadData( XmlReader reader, Type type, object obj )
82  {
83  Dictionary<string, string> metadata = null;
84  int depth = reader.Depth;
85 
86  while ( reader.Read() )
87  {
88  if ( ( reader.NodeType == XmlNodeType.EndElement ) && ( reader.Depth <= depth ) )
89  break;
90 
91  if ( reader.NodeType != XmlNodeType.Element )
92  continue;
93 
94  if ( reader.Name == "Meta" )
95  {
96  if ( metadata == null ) metadata = new Dictionary<string, string>();
97 
98  reader.MoveToFirstAttribute();
99  do
100  {
101  metadata.Add( reader.Name, reader.ReadContentAsString() );
102  } while ( reader.MoveToNextAttribute() );
103  }
104 
105  if ( reader.IsEmptyElement )
106  continue;
107 
108  Destroyable dObj = obj as Destroyable;
109 
110  // A Type object has no reasonable default value.
111  if ( ( type != typeof( Type ) ) && ( obj == null || ( dObj != null && dObj.IsDestroyed ) ) )
112  {
113  object tag = obj is Tagged ? ( (Tagged)obj ).Tag : null;
114  string stag = tag as string;
115 
116  if ( !TryCreateObject( type, stag, out obj ) )
117  throw new NullReferenceException( "Object of type " + type.Name + " must be initialized before it can be loaded since there is no factory method or default constructor for it." );
118  }
119 
120  if ( reader.Name == "Field" || reader.Name == "Property" )
121  {
122  string mName = reader.GetAttribute( "Name" );
123  Type mType = TypeHelper.Parse( reader.GetAttribute( "Type" ) );
124 
125  if ( reader.Name == "Property" )
126  {
127  PropertyInfo propInfo = type.GetProperty( mName, BindingFlags.GetProperty | AllOfInstance );
128  if ( propInfo == null ) throw new ArgumentException( type.Name + " does not contain property " + mName );
129  object mValue = propInfo.GetValue( obj, null );
130  mValue = LoadData( reader, mType, mValue );
131  propInfo.SetValue( obj, mValue, null );
132  }
133  else
134  {
135  FieldInfo fieldInfo = type.GetField( mName, BindingFlags.GetField | AllOfInstance );
136  if ( fieldInfo == null ) throw new ArgumentException( type.Name + " does not contain field " + mName );
137  object mValue = fieldInfo.GetValue( obj );
138  mValue = LoadData( reader, mType, mValue );
139  fieldInfo.SetValue( obj, mValue );
140  }
141  }
142  else if ( reader.Name == "SerializedObject" )
143  {
144  // Used for legacy support only
145 
146  reader.Read();
147  object objValue = null;
148 
149  using ( MemoryStream dataStream = new MemoryStream( Encoding.Unicode.GetBytes( reader.Value ) ) )
150  {
151  XmlSerializer serializer = new XmlSerializer( type );
152  objValue = serializer.Deserialize( dataStream );
153  }
154 
155  while ( !reader.EOF && reader.NodeType != XmlNodeType.EndElement ) reader.Read();
156 
157  obj = objValue;
158  }
159  else if ( reader.Name == "Value" )
160  {
161  reader.Read();
162 
163  if ( type.IsEnum )
164  obj = Enum.Parse( type, reader.Value, false );
165  else
166  obj = GenParse( type, reader.Value );
167 
168  while ( !reader.EOF && reader.NodeType != XmlNodeType.EndElement ) reader.Read();
169  }
170  else if ( reader.Name == "Array" )
171  {
172  Type mType = TypeHelper.Parse( reader.GetAttribute( "Type" ) );
173  obj = LoadArray( reader, mType, obj );
174  }
175  else if ( reader.Name == "List" )
176  {
177  Type mType = TypeHelper.Parse( reader.GetAttribute( "Type" ) );
178  int itemCount = int.Parse( reader.GetAttribute( "Count" ) );
179  obj = LoadList( reader, mType, itemCount, obj );
180  }
181  }
182 
183  if ( metadata != null )
184  {
185  foreach ( KeyValuePair<string, string> entry in metadata )
186  applyMetadata( type, obj, entry.Key, entry.Value );
187  }
188 
189  return obj;
190  }
191 
192  private void applyMetadata( Type type, object obj, string tag, string value )
193  {
194 #if JYPELI
195  if ( TypeHelper.InheritsFrom( type, typeof( GameObject ) ) )
196  {
197  // Game object tags
198  GameObject gobj = (GameObject)obj;
199 
200  if ( tag == "AddedToGame" && tagTrue( value ) && !gobj.IsAddedToGame )
201  Game.Instance.Add( gobj );
202  }
203 #endif
204  }
205 
206  private bool tagTrue( string value )
207  {
208  return value == "1" || value.ToLower() == "true";
209  }
210 
211  internal void SaveData( XmlWriter writer, Type type, object obj, bool saveAllFields )
212  {
213  if ( obj is Array )
214  {
215  SaveArray( writer, type, obj );
216  return;
217  }
218 
219  if ( obj is IList )
220  {
221  SaveList( writer, type, obj );
222  return;
223  }
224 
225  if ( obj is Type )
226  {
227  writer.WriteElementString( "Value", ( obj as Type ).AssemblyQualifiedName );
228  }
229 
230  bool hasSaveAttrib = saveAllFields || type.GetCustomAttributes( typeof( SaveAttribute ), true ).Length > 0;
231  bool hasSaveAllFields = saveAllFields || type.GetCustomAttributes( typeof( SaveAllFieldsAttribute ), true ).Length > 0;
232 
233  if ( type.IsClass && type != typeof( string ) && !hasSaveAttrib )
234  return;
235 
236  if ( !hasSaveAttrib || ( type.IsPrimitive && type.IsEnum ) )
237  {
238  writer.WriteElementString( "Value", GenToString( type, obj ) );
239  return;
240  }
241 
242  writeMetadata( writer, type, obj );
243 
244  foreach ( PropertyInfo prop in type.GetProperties( BindingFlags.GetProperty | AllOfInstance ) )
245  {
246  if ( IsIllegalType( prop.PropertyType ) )
247  continue;
248 
249  object[] attribs = prop.GetCustomAttributes( true );
250  if ( prop.GetCustomAttributes( typeof( SaveAttribute ), true ).Length == 0 )
251  continue;
252 
253  object propValue = prop.GetValue( obj, null );
254 
255  if ( propValue == null )
256  continue;
257 
258  Type st = propValue.GetType();
259  SaveMember( writer, "Property", st, prop, propValue, saveAllFields );
260  }
261 
262  foreach ( FieldInfo field in type.GetFields( BindingFlags.GetField | AllOfInstance ) )
263  {
264  if ( IsIllegalType( field.FieldType ) )
265  continue;
266 
267  if ( !hasSaveAllFields && field.GetCustomAttributes( typeof( SaveAttribute ), true ).Length == 0 )
268  continue;
269 
270  object fieldValue = field.GetValue( obj );
271 
272  if ( fieldValue == null )
273  continue;
274 
275  Type st = fieldValue.GetType();
276  SaveMember( writer, "Field", st, field, fieldValue, saveAllFields );
277  }
278  }
279 
280  private bool IsIllegalType( Type type )
281  {
282  return ( type == typeof( IntPtr ) || type == typeof( UIntPtr ) );
283  }
284 
285  private void SaveMember( XmlWriter writer, string elementName, Type memberType, MemberInfo member, object memberValue, bool saveAllFields )
286  {
287  writer.WriteStartElement( elementName );
288  writer.WriteAttributeString( "Name", member.Name );
289  writer.WriteAttributeString( "Type", TypeHelper.ToString( memberType ) );
290  SaveData( writer, memberType, memberValue, saveAllFields || member.GetCustomAttributes( typeof( SaveAllFieldsAttribute ), true ).Length > 0 );
291  writer.WriteEndElement();
292  }
293 
294  private void writeMetadata( XmlWriter writer, Type type, object obj )
295  {
296 #if JYPELI
297  if ( TypeHelper.InheritsFrom( type, typeof( GameObject ) ) && ( (GameObject)obj ).IsAddedToGame )
298  {
299  writer.WriteStartElement( "Meta" );
300  writer.WriteAttributeString( "AddedToGame", "1" );
301  writer.WriteEndElement();
302  }
303 #endif
304  }
305 
306  internal object LoadArray( XmlReader reader, Type containerType, object obj )
307  {
308  Type designatedItemType = null;
309  Array array = (Array)obj;
310  int index = 0;
311 
312  for ( int readItems = 0;
313  readItems < array.GetLength( 0 ) &&
314  reader.Read() && !( reader.NodeType == XmlNodeType.EndElement && reader.Name == "Array" ); )
315  {
316  if ( reader.NodeType != XmlNodeType.Element )
317  continue;
318  if ( reader.Name != "Item" )
319  {
320  string name = reader.GetAttribute( "Name" );
321  if ( name == null ) name = "(undefined)";
322  throw new XmlException( "Unexpected subelement in List block: " + reader.Name + ", Name = " + name );
323  }
324 
325  index = Int16.Parse( reader.GetAttribute( "Index" ) );
326  designatedItemType = TypeHelper.Parse( reader.GetAttribute( "Type" ) );
327 
328  object item = array.GetValue( index );
329  if ( item != null )
330  {
331  Type realItemType = item.GetType();
332  if ( designatedItemType != realItemType )
333  throw new XmlException( "Array item type mismatch: expected " + designatedItemType.Name + ", got " + realItemType.Name + " at index " + index.ToString() );
334  }
335 
336  item = LoadData( reader, designatedItemType, item );
337  array.SetValue( item, index );
338 
339  readItems++;
340  }
341 
342  return array;
343  }
344 
345  private void FixListSize( ref IList list, int itemCount )
346  {
347  while ( list.Count < itemCount )
348  {
349  list.Add( null );
350  }
351 
352  while ( list.Count > itemCount )
353  {
354  list.RemoveAt( list.Count - 1 );
355  }
356  }
357 
358  internal object LoadList( XmlReader reader, Type containerType, int itemCount, object obj )
359  {
360  object[] listObjects = new object[itemCount];
361  IList list = (IList)obj;
362 
363  while ( list.Count > itemCount )
364  {
365  int removeIndex = list.Count - 1;
366  if ( list[removeIndex] is Destroyable )
367  {
368  ( (Destroyable)( list[removeIndex] ) ).Destroy();
369  }
370  list.RemoveAt( removeIndex );
371  }
372 
373  for ( int i = 0; i < Math.Min( itemCount, list.Count ); i++ )
374  listObjects[i] = list[i];
375  for ( int i = list.Count; i < itemCount; i++ )
376  listObjects[i] = null;
377 
378  while ( reader.Read() && !( reader.NodeType == XmlNodeType.EndElement && reader.Name == "List" ) )
379  {
380  if ( reader.NodeType != XmlNodeType.Element )
381  continue;
382  if ( reader.Name != "Item" )
383  {
384  string name = reader.GetAttribute( "Name" );
385  if ( name == null ) name = "(undefined)";
386  throw new XmlException( "Unexpected subelement in List block: " + reader.Name + ", Name = " + name );
387  }
388 
389  string tag = reader.GetAttribute( "Tag" );
390  Type itemType = TypeHelper.Parse( reader.GetAttribute( "Type" ) );
391  int itemindex = int.Parse( reader.GetAttribute( "Index" ) );
392 
393  if ( tag != null && listObjects[itemindex] == null )
394  {
395  if ( !TryCreateObject( itemType, tag, out listObjects[itemindex] ) )
396  throw new MissingMethodException( String.Format("No factory method for type {0}, tag {1}", itemType.Name, tag) );
397  }
398 
399  listObjects[itemindex] = LoadData( reader, itemType, listObjects[itemindex] );
400  }
401 
402  list.Clear();
403 
404  for ( int i = 0; i < listObjects.Length; i++ )
405  list.Add( listObjects[i] );
406 
407  return list;
408  }
409 
410  bool TryCreateObject( Type type, string tag, out object obj )
411  {
412  if ( type == typeof( string ) )
413  {
414  // Strings are immutable, and therefore have no default constructor
415  obj = "";
416  return true;
417  }
418  else
419  {
420  try
421  {
422  // Try to create using a factory method
423  obj = Factory.FactoryCreate( type, tag );
424  return true;
425  }
426  catch ( KeyNotFoundException )
427  {
428  try
429  {
430  // Try the default constructor
431  obj = Activator.CreateInstance( type, true );
432  return true;
433  }
434  catch ( MissingMethodException )
435  {
436  obj = null;
437  return false;
438  }
439  }
440  }
441  }
442 
443  internal void SaveArray( XmlWriter writer, Type containerType, object obj )
444  {
445  Array array = (Array)obj;
446 
447  writer.WriteStartElement( "Array" );
448  writer.WriteAttributeString( "Type", TypeHelper.ToString( containerType ) );
449 
450  for ( int i = 0; i < array.GetLength( 0 ); i++ )
451  {
452  object item = array.GetValue( i );
453  Type realItemType = item.GetType();
454  writer.WriteStartElement( "Item" );
455  writer.WriteAttributeString( "Index", i.ToString() );
456  writer.WriteAttributeString( "Type", TypeHelper.ToString( realItemType ) );
457  if ( item is Tagged ) writer.WriteAttributeString( "Tag", ( (Tagged)item ).Tag.ToString() );
458  SaveData( writer, realItemType, item, false );
459  writer.WriteEndElement();
460  }
461 
462  writer.WriteEndElement();
463  }
464 
465  internal void SaveList( XmlWriter writer, Type containerType, object obj )
466  {
467  IList list = (IList)obj;
468 
469  writer.WriteStartElement( "List" );
470  writer.WriteAttributeString( "Type", TypeHelper.ToString( containerType ) );
471  writer.WriteAttributeString( "Count", list.Count.ToString() );
472 
473  for ( int i = 0; i < list.Count; i++ )
474  {
475  Type realItemType = list[i].GetType();
476  writer.WriteStartElement( "Item" );
477  writer.WriteAttributeString( "Type", TypeHelper.ToString( realItemType ) );
478  writer.WriteAttributeString( "Index", i.ToString() );
479  if ( list[i] is Tagged ) writer.WriteAttributeString( "Tag", ( (Tagged)list[i] ).Tag.ToString() );
480  SaveData( writer, realItemType, list[i], false );
481  writer.WriteEndElement();
482  }
483 
484  writer.WriteEndElement();
485  }
486  }
487 }
Jypeli.StorageFile
Tiedosto.
Definition: StorageFile.cs:17
Jypeli.Destroyable.IsDestroyed
bool IsDestroyed
Definition: Destroyable.cs:10
Jypeli.StorageFile.Dispose
void Dispose()
Definition: StorageFile.cs:33
Jypeli.StorageFile.SaveData
void SaveData(XmlWriter writer, Type type, object obj, bool saveAllFields)
Definition: StorageFile.cs:211
Jypeli.SaveAllFieldsAttribute
Definition: SaveAttribute.cs:18
Jypeli.StorageFile.applyMetadata
void applyMetadata(Type type, object obj, string tag, string value)
Definition: StorageFile.cs:192
Jypeli.StorageFile.writeMetadata
void writeMetadata(XmlWriter writer, Type type, object obj)
Definition: StorageFile.cs:294
Jypeli.StorageFile.TryCreateObject
bool TryCreateObject(Type type, string tag, out object obj)
Definition: StorageFile.cs:410
Jypeli.TypeHelper.InheritsFrom
static bool InheritsFrom(Type actual, Type expected)
Definition: TypeHelper.cs:10
Jypeli.StorageFile.Close
void Close()
Definition: StorageFile.cs:28
Jypeli.StorageFile.LoadList
object LoadList(XmlReader reader, Type containerType, int itemCount, object obj)
Definition: StorageFile.cs:358
Jypeli.StorageFile.Stream
Stream Stream
Definition: StorageFile.cs:19
Jypeli
Definition: Automobile.cs:5
Jypeli.StorageFile.Name
string Name
Definition: StorageFile.cs:18
Jypeli.StorageFile.IsIllegalType
bool IsIllegalType(Type type)
Definition: StorageFile.cs:280
Jypeli.Factory.FactoryCreate
static object FactoryCreate(Type type, string tag)
Definition: Factory.cs:83
Jypeli.GameObject.IsAddedToGame
bool IsAddedToGame
Onko olio lisätty peliin.
Definition: GameConnection.cs:8
Jypeli.Game.Instance
static Game Instance
Käynnissä olevan pelin pääolio.
Definition: Game.cs:90
Jypeli.StorageFile.AllOfInstance
static BindingFlags AllOfInstance
Definition: StorageFile.cs:38
Jypeli.Game.Add
void Add(Light light)
Lisää valon peliin. Nykyisellään valoja voi olla ainoastaan yksi kappale. Toistaiseksi ei tuettu Wind...
Definition: Effects.cs:27
Jypeli.StorageFile.GenToString
string GenToString(Type type, object obj)
Definition: StorageFile.cs:40
Jypeli.StorageFile.SaveArray
void SaveArray(XmlWriter writer, Type containerType, object obj)
Definition: StorageFile.cs:443
Jypeli.StorageFile.FixListSize
void FixListSize(ref IList list, int itemCount)
Definition: StorageFile.cs:345
Jypeli.TypeHelper.Parse
static Type Parse(string typeStr)
Definition: TypeHelper.cs:65
Jypeli.Tagged
Rajapinta olioille, joilla on Tag-ominaisuus.
Definition: Tagged.cs:7
Jypeli.Destroyable
Rajapinta olioille, jotka ovat tuhottavissa.
Definition: Destroyable.cs:9
Jypeli.StorageFile.SaveList
void SaveList(XmlWriter writer, Type containerType, object obj)
Definition: StorageFile.cs:465
Jypeli.Tagged.Tag
object Tag
Definition: Tagged.cs:8
Jypeli.Factory
Definition: Factory.cs:43
Jypeli.SaveAttribute
Definition: SaveAttribute.cs:9
System
Definition: CFFauxAttributes.cs:29
Jypeli.StorageFile.LoadArray
object LoadArray(XmlReader reader, Type containerType, object obj)
Definition: StorageFile.cs:306
Jypeli.StorageFile.GenParse
object GenParse(Type type, string str)
Definition: StorageFile.cs:59
Jypeli.StorageFile.tagTrue
bool tagTrue(string value)
Definition: StorageFile.cs:206
Jypeli.GameObject
Pelialueella liikkuva olio. Käytä fysiikkapeleissä PhysicsObject-olioita.
Definition: Appearance.cs:34
Jypeli.TypeHelper.ToString
static string ToString(Type type)
Definition: TypeHelper.cs:24
Jypeli.Game
Definition: Content.cs:46
Jypeli.StorageFile.StorageFile
StorageFile(string name, Stream stream)
Definition: StorageFile.cs:21
Jypeli.TypeHelper
Definition: TypeHelper.cs:7
Jypeli.StorageFile.SaveMember
void SaveMember(XmlWriter writer, string elementName, Type memberType, MemberInfo member, object memberValue, bool saveAllFields)
Definition: StorageFile.cs:285
Jypeli.StorageFile.LoadData
object LoadData(XmlReader reader, Type type, object obj)
Definition: StorageFile.cs:81