5 // Iain McCoy (iain@mccoy.id.au)
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 using System.Diagnostics;
31 using System.Reflection;
34 using System.Collections;
36 using System.CodeDom.Compiler;
37 using System.Windows.Serialization;
40 namespace Mono.Windows.Serialization {
41 public class CodeWriter {
43 ICodeGenerator generator;
46 ArrayList objects = new ArrayList();
47 Hashtable nameClashes = new Hashtable();
51 CodeTypeDeclaration type;
52 CodeConstructor constructor;
54 public static string Parse(XmlTextReader reader, ICodeGenerator generator, bool isPartial)
56 CodeWriter cw = new CodeWriter(reader, generator, isPartial);
57 return ((StringWriter)cw.writer).ToString();
60 // pushes: the code writer
61 private void init(ICodeGenerator generator, bool isPartial)
63 this.generator = generator;
64 this.isPartial = isPartial;
65 this.writer = new StringWriter();
66 code = new CodeCompileUnit();
71 private CodeWriter(XmlTextReader reader, ICodeGenerator generator, bool isPartial)
73 init(generator, isPartial);
74 XamlParser p = new XamlParser(reader);
80 Debug.WriteLine("CodeWriter: INCOMING " + n.GetType());
81 if (n is XamlDocumentStartNode) {
82 Debug.WriteLine("CodeWriter: document begins");
84 } else if (n is XamlElementStartNode && n.Depth == 0) {
85 Debug.WriteLine("CodeWriter: element begins as top-level");
86 CreateTopLevel(((XamlElementStartNode)n).ElementType, ((XamlElementStartNode)n).name);
87 } else if (n is XamlElementStartNode && ((XamlElementStartNode)n).propertyObject) {
88 Debug.WriteLine("CodeWriter: element begins as property value");
89 CreatePropertyObject(((XamlElementStartNode)n).ElementType, ((XamlElementStartNode)n).name);
90 } else if (n is XamlElementStartNode) {
91 Debug.WriteLine("CodeWriter: element begins");
92 CreateObject(((XamlElementStartNode)n).ElementType, ((XamlElementStartNode)n).name);
93 } else if (n is XamlPropertyNode && ((XamlPropertyNode)n).PropInfo != null) {
94 Debug.WriteLine("CodeWriter: normal property begins");
95 CreateProperty(((XamlPropertyNode)n).PropInfo);
96 } else if (n is XamlPropertyNode && ((XamlPropertyNode)n).DP != null) {
97 Debug.WriteLine("CodeWriter: dependency property begins");
98 DependencyProperty dp = ((XamlPropertyNode)n).DP;
99 Type typeAttachedTo = dp.OwnerType;
100 string propertyName = ((XamlPropertyNode)n).PropertyName;
102 CreateDependencyProperty(typeAttachedTo, propertyName, dp.PropertyType);
103 } else if (n is XamlClrEventNode && !(((XamlClrEventNode)n).EventMember is EventInfo)) {
104 Debug.WriteLine("CodeWriter: delegate property");
105 CreatePropertyDelegate(((XamlClrEventNode)n).Value, ((PropertyInfo)((XamlClrEventNode)n).EventMember).PropertyType);
109 } else if (n is XamlClrEventNode) {
110 Debug.WriteLine("CodeWriter: event");
111 CreateEvent((EventInfo)((XamlClrEventNode)n).EventMember);
112 CreateEventDelegate(((XamlClrEventNode)n).Value, ((EventInfo)((XamlClrEventNode)n).EventMember).EventHandlerType);
115 } else if (n is XamlTextNode && ((XamlTextNode)n).mode == XamlParseMode.Object){
116 Debug.WriteLine("CodeWriter: text for object");
117 CreateObjectText(((XamlTextNode)n).TextContent);
118 } else if (n is XamlTextNode && ((XamlTextNode)n).mode == XamlParseMode.Property){
119 Debug.WriteLine("CodeWriter: text for property");
120 CreatePropertyText(((XamlTextNode)n).TextContent, ((XamlTextNode)n).finalType);
122 } else if (n is XamlTextNode && ((XamlTextNode)n).mode == XamlParseMode.DependencyProperty){
123 Debug.WriteLine("CodeWriter: text for dependency property");
124 CreateDependencyPropertyText(((XamlTextNode)n).TextContent, ((XamlTextNode)n).finalType);
125 EndDependencyProperty();
126 } else if (n is XamlPropertyComplexEndNode) {
127 Debug.WriteLine("CodeWriter: end complex property");
128 Debug.WriteLine("CodeWriter: final type is " + ((XamlPropertyComplexEndNode)n).finalType);
129 EndPropertyObject(((XamlPropertyComplexEndNode)n).finalType);
131 } else if (n is XamlLiteralContentNode) {
132 Debug.WriteLine("CodeWriter: literal content");
133 CreateCode(((XamlLiteralContentNode)n).Content);
134 } else if (n is XamlElementEndNode) {
135 Debug.WriteLine("CodeWriter: end element");
136 if (!((XamlElementEndNode)n).propertyObject)
138 } else if (n is XamlDocumentEndNode) {
139 Debug.WriteLine("CodeWriter: end document");
142 throw new Exception("Unknown node " + n.GetType());
149 // pushes: a CodeVariableReferenceExpression to the present
151 public void CreateTopLevel(Type parent, string className)
154 if (className == null) {
155 className = "derived" + parent.Name;
157 int endNamespaceName = className.LastIndexOf(".");
159 if (endNamespaceName < 0) {
160 clrNamespace = "DefaultNamespace";
162 clrNamespace = className.Substring(0,
164 className = className.Substring(endNamespaceName+1);
166 CodeNamespace ns = new CodeNamespace(clrNamespace);
167 ((CodeCompileUnit)objects[0]).Namespaces.Add(ns);
169 type = new CodeTypeDeclaration(className);
172 type.IsPartial = isPartial;
174 throw new Exception("Cannot create partial class");
177 type.BaseTypes.Add(new CodeTypeReference(parent));
178 constructor = new CodeConstructor();
179 type.Members.Add(constructor);
182 push(new CodeThisReferenceExpression());
185 // bottom of stack holds CodeVariableReferenceExpression
186 // pushes a reference to the new current type
187 public void CreateObject(Type type, string varName)
191 if (varName == null) {
192 isDefaultName = true;
193 varName = Char.ToLower(type.Name[0]) + type.Name.Substring(1);
194 // make sure something sensible happens when class
195 // names start with a lowercase letter
196 if (varName == type.Name)
197 varName = "_" + varName;
199 isDefaultName = false;
203 if (!nameClashes.ContainsKey(varName))
204 nameClashes[varName] = 0;
206 nameClashes[varName] = 1 + (int)nameClashes[varName];
207 varName += (int)nameClashes[varName];
212 CodeVariableDeclarationStatement declaration =
213 new CodeVariableDeclarationStatement(type,
215 new CodeObjectCreateExpression(type));
216 constructor.Statements.Add(declaration);
218 CodeMemberField declaration = new CodeMemberField(type, varName);
219 declaration.InitExpression = new CodeObjectCreateExpression(type);
220 this.type.Members.Add(declaration);
222 CodeVariableReferenceExpression varRef = new CodeVariableReferenceExpression(varName);
223 CodeMethodInvokeExpression addChild = new CodeMethodInvokeExpression(
224 (CodeExpression)peek(),
227 constructor.Statements.Add(addChild);
231 // top of stack is a reference to an object
232 // pushes a reference to the property
233 public void CreateProperty(PropertyInfo property)
236 CodePropertyReferenceExpression prop = new CodePropertyReferenceExpression(
237 (CodeExpression)peek(),
242 // top of stack is a reference to an object
243 // pushes a reference to the event
244 public void CreateEvent(EventInfo evt)
247 CodeEventReferenceExpression expr = new CodeEventReferenceExpression(
248 (CodeExpression)peek(),
253 // top of stack is a reference to an object
254 // pushes a reference to the expression that
255 // will set the property and a reference to
256 // the name of the temp variable to hold the
258 public void CreateDependencyProperty(Type attachedTo, string propertyName, Type propertyType)
261 string varName = "temp";
262 varName += tempIndex;
264 CodeVariableDeclarationStatement decl = new CodeVariableDeclarationStatement(propertyType, varName);
265 constructor.Statements.Add(decl);
268 CodeMethodInvokeExpression call = new CodeMethodInvokeExpression(
269 new CodeTypeReferenceExpression(attachedTo),
270 "Set" + propertyName,
271 (CodeExpression)peek(),
272 new CodeVariableReferenceExpression(varName));
275 push(new CodeVariableReferenceExpression(varName));
278 // pops 2 items: the name of the property, and the object to attach to
279 public void EndDependencyProperty()
282 pop(); // pop the variable name - we don't need it since it's already baked into the call
283 CodeExpression call = (CodeExpression)pop();
284 constructor.Statements.Add(call);
287 // top of stack must be an object reference
288 public void CreateObjectText(string text)
291 CodeVariableReferenceExpression var = (CodeVariableReferenceExpression)peek();
292 CodeMethodInvokeExpression call = new CodeMethodInvokeExpression(
295 new CodePrimitiveExpression(text));
296 constructor.Statements.Add(call);
299 // top of stack is reference to an event
300 public void CreateEventDelegate(string functionName, Type eventDelegateType)
303 CodeExpression expr = new CodeObjectCreateExpression(
305 new CodeMethodReferenceExpression(
306 new CodeThisReferenceExpression(),
308 CodeAttachEventStatement attach = new CodeAttachEventStatement(
309 (CodeEventReferenceExpression)peek(),
311 constructor.Statements.Add(attach);
314 // top of stack is reference to a property
315 public void CreatePropertyDelegate(string functionName, Type propertyType)
318 CodeExpression expr = new CodeObjectCreateExpression(
320 new CodeMethodReferenceExpression(
321 new CodeThisReferenceExpression(),
323 CodeAssignStatement assignment = new CodeAssignStatement(
324 (CodeExpression)peek(),
326 constructor.Statements.Add(assignment);
329 private CodeExpression fetchConverter(Type propertyType)
331 return new CodeMethodInvokeExpression(
332 new CodeMethodReferenceExpression(
333 new CodeTypeReferenceExpression(typeof(System.ComponentModel.TypeDescriptor)),
335 new CodeTypeOfExpression(propertyType));
338 // top of stack is reference to a property
339 public void CreatePropertyText(string text, Type propertyType)
342 CreateDependencyPropertyText(text, propertyType);
344 // top of stack is reference to an attached property
345 public void CreateDependencyPropertyText(string text, Type propertyType)
348 CodeExpression expr = new CodePrimitiveExpression(text);
349 if (propertyType != typeof(string)) {
350 expr = new CodeCastExpression(
351 new CodeTypeReference(propertyType),
352 new CodeMethodInvokeExpression(
353 fetchConverter(propertyType),
357 CodeAssignStatement assignment = new CodeAssignStatement(
358 (CodeExpression)peek(),
361 constructor.Statements.Add(assignment);
364 public void CreatePropertyObject(Type type, string varName)
368 if (varName == null) {
369 isDefaultName = true;
370 varName = Char.ToLower(type.Name[0]) + type.Name.Substring(1);
371 // make sure something sensible happens when class
372 // names start with a lowercase letter
373 if (varName == type.Name)
374 varName = "_" + varName;
376 isDefaultName = false;
380 if (!nameClashes.ContainsKey(varName))
381 nameClashes[varName] = 0;
382 nameClashes[varName] = 1 + (int)nameClashes[varName];
383 varName += (int)nameClashes[varName];
388 CodeVariableDeclarationStatement declaration =
389 new CodeVariableDeclarationStatement(type,
391 new CodeObjectCreateExpression(type));
392 constructor.Statements.Add(declaration);
394 CodeMemberField declaration = new CodeMemberField(type, varName);
395 declaration.InitExpression = new CodeObjectCreateExpression(type);
396 this.type.Members.Add(declaration);
398 CodeVariableReferenceExpression varRef = new CodeVariableReferenceExpression(varName);
405 public void EndPropertyObject(Type destType)
408 CodeExpression varRef = (CodeExpression)pop();
409 Type sourceType = (Type)pop();
411 Debug.WriteLine("CodeWriter: " + destType + "->" + sourceType);
415 if (sourceType == destType || sourceType.IsSubclassOf(destType))
418 expr = new CodeCastExpression(
419 new CodeTypeReference(destType),
420 new CodeMethodInvokeExpression(
421 fetchConverter(sourceType),
424 new CodeTypeOfExpression(destType)));
425 CodeAssignStatement assignment = new CodeAssignStatement(
426 (CodeExpression)peek(),
428 constructor.Statements.Add(assignment);
431 public void EndObject()
437 public void EndProperty()
443 public void EndEvent()
452 generator.GenerateCodeFromCompileUnit(code, writer, null);
456 public void CreateCode(string code)
459 type.Members.Add(new CodeSnippetTypeMember(code));
464 Debug.WriteLine("CodeWriter: " + new System.Diagnostics.StackTrace());
469 object v = objects[objects.Count - 1];
470 objects.RemoveAt(objects.Count - 1);
471 Debug.WriteLine("CodeWriter: POPPING");
474 private void push(object v)
476 Debug.WriteLine("CodeWriter: PUSHING " + v);
479 private object peek()
483 private object peek(int i)
485 return objects[objects.Count - 1 - i];