1 // 2 // Copyright (C) 2010 Novell Inc. http://novell.com 3 // 4 // Permission is hereby granted, free of charge, to any person obtaining 5 // a copy of this software and associated documentation files (the 6 // "Software"), to deal in the Software without restriction, including 7 // without limitation the rights to use, copy, modify, merge, publish, 8 // distribute, sublicense, and/or sell copies of the Software, and to 9 // permit persons to whom the Software is furnished to do so, subject to 10 // the following conditions: 11 // 12 // The above copyright notice and this permission notice shall be 13 // included in all copies or substantial portions of the Software. 14 // 15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 // 23 using System; 24 using System.Collections.Generic; 25 using System.IO; 26 using System.Linq; 27 using System.Xml; 28 29 /* 30 31 * State transition 32 33 Unlike XmlWriter, XAML nodes are not immediately writable because object 34 output has to be delayed to be determined whether it should write 35 an attribute or an element. 36 37 ** NamespaceDeclarations 38 39 NamespaceDeclaration does not immediately participate in the state transition 40 but some write methods reject stored namespaces (e.g. WriteEndObject cannot 41 handle them). In such cases, they throw InvalidOperationException, while the 42 writer throws XamlXmlWriterException for usual state transition. 43 44 Though they still seems to affect some outputs. If a member with simple 45 value is written after a namespace, then it becomes an element, not attribute. 46 47 ** state transition 48 49 states are: Initial, ObjectStarted, MemberStarted, ValueWritten, MemberDone, End 50 51 Initial + StartObject -> ObjectStarted : push(xt) 52 ObjectStarted + StartMember -> MemberStarted : push(xm) 53 ObjectStarted + EndObject -> ObjectWritten or End : pop() 54 MemberStarted + StartObject -> ObjectStarted : push(xt) 55 MemberStarted + Value -> ValueWritten 56 MemberStarted + GetObject -> MemberDone : pop() 57 ObjectWritten + StartObject -> ObjectStarted : push(x) 58 ObjectWritten + Value -> ValueWritten : pop() 59 ObjectWritten + EndMember -> MemberDone : pop() 60 ValueWritten + StartObject -> invalid - or - ObjectStarted : push(x) 61 ValueWritten + Value -> invalid - or - ValueWritten 62 ValueWritten + EndMember -> MemberDone : pop() 63 MemberDone + EndObject -> ObjectWritten or End : pop() // xt 64 MemberDone + StartMember -> MemberStarted : push(xm) 65 66 (in XamlObjectWriter, Value must be followed by EndMember.) 67 68 */ 69 70 namespace System.Xaml 71 { 72 internal class XamlWriterStateManager<TError,TNSError> : XamlWriterStateManager 73 where TError : Exception 74 where TNSError : Exception 75 { XamlWriterStateManager(bool isXmlWriter)76 public XamlWriterStateManager (bool isXmlWriter) 77 : base (isXmlWriter) 78 { 79 } 80 CreateError(string msg)81 public override Exception CreateError (string msg) 82 { 83 return (Exception) Activator.CreateInstance (typeof (TError), new object [] {msg}); 84 } 85 CreateNamespaceError(string msg)86 public override Exception CreateNamespaceError (string msg) 87 { 88 return (Exception) Activator.CreateInstance (typeof (TNSError), new object [] {msg}); 89 } 90 } 91 92 internal enum XamlWriteState 93 { 94 Initial, 95 ObjectStarted, 96 MemberStarted, 97 ObjectWritten, 98 ValueWritten, 99 MemberDone, 100 End 101 } 102 103 internal abstract class XamlWriterStateManager 104 { XamlWriterStateManager(bool isXmlWriter)105 public XamlWriterStateManager (bool isXmlWriter) 106 { 107 allow_ns_at_value = isXmlWriter; 108 allow_object_after_value = isXmlWriter; 109 allow_parallel_values = !isXmlWriter; 110 allow_empty_member = !isXmlWriter; 111 allow_multiple_results = !isXmlWriter; 112 } 113 114 // configuration 115 bool allow_ns_at_value, allow_object_after_value, allow_parallel_values, allow_empty_member, allow_multiple_results; 116 117 // state 118 XamlWriteState state = XamlWriteState.Initial; 119 bool ns_pushed; 120 bool accept_multiple_values; // It is PositionalParameters-specific state. 121 122 public XamlWriteState State { 123 get { return state; } 124 } 125 126 // FIXME: actually this property is a hack. It should preserve stacked flag values for each nested member in current tree state. 127 public bool AcceptMultipleValues { 128 get { return accept_multiple_values; } 129 set { accept_multiple_values = value; } 130 } 131 OnClosingItem()132 public void OnClosingItem () 133 { 134 // somewhat hacky state change to not reject StartMember->EndMember. 135 if (state == XamlWriteState.MemberStarted) 136 state = XamlWriteState.ValueWritten; 137 } 138 EndMember()139 public void EndMember () 140 { 141 RejectNamespaces (XamlNodeType.EndMember); 142 CheckState (XamlNodeType.EndMember); 143 state = XamlWriteState.MemberDone; 144 } 145 EndObject(bool hasMoreNodes)146 public void EndObject (bool hasMoreNodes) 147 { 148 RejectNamespaces (XamlNodeType.EndObject); 149 CheckState (XamlNodeType.EndObject); 150 state = hasMoreNodes ? XamlWriteState.ObjectWritten : allow_multiple_results ? XamlWriteState.Initial : XamlWriteState.End; 151 } 152 GetObject()153 public void GetObject () 154 { 155 CheckState (XamlNodeType.GetObject); 156 RejectNamespaces (XamlNodeType.GetObject); 157 state = XamlWriteState.MemberDone; 158 } 159 StartMember()160 public void StartMember () 161 { 162 CheckState (XamlNodeType.StartMember); 163 state = XamlWriteState.MemberStarted; 164 ns_pushed = false; 165 } 166 StartObject()167 public void StartObject () 168 { 169 CheckState (XamlNodeType.StartObject); 170 state = XamlWriteState.ObjectStarted; 171 ns_pushed = false; 172 } 173 Value()174 public void Value () 175 { 176 CheckState (XamlNodeType.Value); 177 RejectNamespaces (XamlNodeType.Value); 178 state = XamlWriteState.ValueWritten; 179 } 180 Namespace()181 public void Namespace () 182 { 183 if (!allow_ns_at_value && (state == XamlWriteState.ValueWritten || state == XamlWriteState.ObjectStarted)) 184 throw CreateError (String.Format ("Namespace declarations cannot be written at {0} state", state)); 185 ns_pushed = true; 186 } 187 NamespaceCleanedUp()188 public void NamespaceCleanedUp () 189 { 190 ns_pushed = false; 191 } 192 CheckState(XamlNodeType next)193 void CheckState (XamlNodeType next) 194 { 195 switch (state) { 196 case XamlWriteState.Initial: 197 switch (next) { 198 case XamlNodeType.StartObject: 199 return; 200 } 201 break; 202 case XamlWriteState.ObjectStarted: 203 switch (next) { 204 case XamlNodeType.StartMember: 205 case XamlNodeType.EndObject: 206 return; 207 } 208 break; 209 case XamlWriteState.MemberStarted: 210 switch (next) { 211 case XamlNodeType.StartObject: 212 case XamlNodeType.Value: 213 case XamlNodeType.GetObject: 214 return; 215 case XamlNodeType.EndMember: 216 if (allow_empty_member) 217 return; 218 break; 219 } 220 break; 221 case XamlWriteState.ObjectWritten: 222 switch (next) { 223 case XamlNodeType.StartObject: 224 case XamlNodeType.Value: 225 case XamlNodeType.EndMember: 226 return; 227 } 228 break; 229 case XamlWriteState.ValueWritten: 230 switch (next) { 231 case XamlNodeType.Value: 232 if (allow_parallel_values | accept_multiple_values) 233 return; 234 break; 235 case XamlNodeType.StartObject: 236 if (allow_object_after_value) 237 return; 238 break; 239 case XamlNodeType.EndMember: 240 return; 241 } 242 break; 243 case XamlWriteState.MemberDone: 244 switch (next) { 245 case XamlNodeType.StartMember: 246 case XamlNodeType.EndObject: 247 return; 248 } 249 break; 250 } 251 throw CreateError (String.Format ("{0} is not allowed at current state {1}", next, state)); 252 } 253 RejectNamespaces(XamlNodeType next)254 void RejectNamespaces (XamlNodeType next) 255 { 256 if (ns_pushed) { 257 // strange, but on WriteEndMember it throws XamlXmlWriterException, while for other nodes it throws IOE. 258 string msg = String.Format ("Namespace declarations cannot be written before {0}", next); 259 if (next == XamlNodeType.EndMember) 260 throw CreateError (msg); 261 else 262 throw CreateNamespaceError (msg); 263 } 264 } 265 CreateError(string msg)266 public abstract Exception CreateError (string msg); CreateNamespaceError(string msg)267 public abstract Exception CreateNamespaceError (string msg); 268 } 269 } 270