2 // Copyright (C) 2010 Novell Inc. http://novell.com
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:
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
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.
24 using System.Collections.Generic;
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.
37 ** NamespaceDeclarations
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.
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.
49 states are: Initial, ObjectStarted, MemberStarted, ValueWritten, MemberDone, End
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)
66 (in XamlObjectWriter, Value must be followed by EndMember.)
72 internal class XamlWriterStateManager<TError,TNSError> : XamlWriterStateManager
73 where TError : Exception
74 where TNSError : Exception
76 public XamlWriterStateManager (bool isXmlWriter)
81 public override Exception CreateError (string msg)
83 return (Exception) Activator.CreateInstance (typeof (TError), new object [] {msg});
86 public override Exception CreateNamespaceError (string msg)
88 return (Exception) Activator.CreateInstance (typeof (TNSError), new object [] {msg});
92 internal enum XamlWriteState
103 internal abstract class XamlWriterStateManager
105 public XamlWriterStateManager (bool isXmlWriter)
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;
115 bool allow_ns_at_value, allow_object_after_value, allow_parallel_values, allow_empty_member, allow_multiple_results;
118 XamlWriteState state = XamlWriteState.Initial;
120 bool accept_multiple_values; // It is PositionalParameters-specific state.
122 public XamlWriteState State {
123 get { return state; }
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; }
132 public void OnClosingItem ()
134 // somewhat hacky state change to not reject StartMember->EndMember.
135 if (state == XamlWriteState.MemberStarted)
136 state = XamlWriteState.ValueWritten;
139 public void EndMember ()
141 RejectNamespaces (XamlNodeType.EndMember);
142 CheckState (XamlNodeType.EndMember);
143 state = XamlWriteState.MemberDone;
146 public void EndObject (bool hasMoreNodes)
148 RejectNamespaces (XamlNodeType.EndObject);
149 CheckState (XamlNodeType.EndObject);
150 state = hasMoreNodes ? XamlWriteState.ObjectWritten : allow_multiple_results ? XamlWriteState.Initial : XamlWriteState.End;
153 public void GetObject ()
155 CheckState (XamlNodeType.GetObject);
156 RejectNamespaces (XamlNodeType.GetObject);
157 state = XamlWriteState.MemberDone;
160 public void StartMember ()
162 CheckState (XamlNodeType.StartMember);
163 state = XamlWriteState.MemberStarted;
167 public void StartObject ()
169 CheckState (XamlNodeType.StartObject);
170 state = XamlWriteState.ObjectStarted;
176 CheckState (XamlNodeType.Value);
177 RejectNamespaces (XamlNodeType.Value);
178 state = XamlWriteState.ValueWritten;
181 public void Namespace ()
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));
188 public void NamespaceCleanedUp ()
193 void CheckState (XamlNodeType next)
196 case XamlWriteState.Initial:
198 case XamlNodeType.StartObject:
202 case XamlWriteState.ObjectStarted:
204 case XamlNodeType.StartMember:
205 case XamlNodeType.EndObject:
209 case XamlWriteState.MemberStarted:
211 case XamlNodeType.StartObject:
212 case XamlNodeType.Value:
213 case XamlNodeType.GetObject:
215 case XamlNodeType.EndMember:
216 if (allow_empty_member)
221 case XamlWriteState.ObjectWritten:
223 case XamlNodeType.StartObject:
224 case XamlNodeType.Value:
225 case XamlNodeType.EndMember:
229 case XamlWriteState.ValueWritten:
231 case XamlNodeType.Value:
232 if (allow_parallel_values | accept_multiple_values)
235 case XamlNodeType.StartObject:
236 if (allow_object_after_value)
239 case XamlNodeType.EndMember:
243 case XamlWriteState.MemberDone:
245 case XamlNodeType.StartMember:
246 case XamlNodeType.EndObject:
251 throw CreateError (String.Format ("{0} is not allowed at current state {1}", next, state));
254 void RejectNamespaces (XamlNodeType next)
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);
262 throw CreateNamespaceError (msg);
266 public abstract Exception CreateError (string msg);
267 public abstract Exception CreateNamespaceError (string msg);