using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml.Schema;
using System.CodeDom;
using System.CodeDom.Compiler;
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.Visitors;
namespace XmlSchemaWrapperGen
{
/// <summary>
/// Generates wrappers class for auto generated
/// proxy classes made from xsd.exe tool.
/// </summary>
public class ImplementationGenerator
{
//List of complex schema object types (classes)
List<string> m_schemaObjectsLst = new List<string>();
//List of schema enumeration types types (enum)
List<string> m_schemaEnumLst = new List<string>();
string m_impNamespaceName = null;
string m_InterfaceNamespaceName = null;
string m_proxyNamespaceName = null;
/// <summary>
/// Guid for the factory class
/// </summary>
Guid m_factoryImpGuid;
/// <summary>
/// Generates wrappers COM objects for auto generated
/// XML proxy classes made from xsd.exe tool.
/// </summary>
/// <param name="proxyFileCode">auto generated proxy file content
/// made from xsd.exe tool
/// </param>
/// <param name="language">The code language</param>
/// <param name="outputFilepath">Path to a file to create that will contain the code</param>
/// <param name="schema">The XML schema</param>
/// <param name="factoryGuid">The COM guid to set in the generated factory class </param>
/// <remarks>
/// The given proxy file content must be in VB.NET or C# language, and .NET 2.0 compatible
/// </remarks>
public void CreateComWrapper(TextReader proxyFileCode, SupportedLanguage language, Guid factoryGuid, string outputFilepath, string schema)
{
m_factoryImpGuid = factoryGuid;
m_schemaObjectsLst.Clear();
m_schemaEnumLst.Clear();
if (proxyFileCode == null) throw new ArgumentNullException("proxyFileCode");
string proxyCode = proxyFileCode.ReadToEnd();
string rootClassName = GetRootElementClassName(new StringReader(proxyCode));
if (string.IsNullOrEmpty(rootClassName))
throw new ArgumentException("Failed locate the class that describes the XML root element in the code file", "proxyFile");
TextReader reader = new StringReader(proxyCode);
CodeNamespace ns = Parse(reader, language).Namespaces[0];
CodeNamespace newNs = TransformToWrapper(ns, schema, rootClassName);
WriteCode(newNs, language, outputFilepath);
}
/// <summary>
/// Generates wrappers COM objects for auto generated
/// XML proxy classes made from xsd.exe tool.
/// </summary>
/// <param name="proxyFileCode">auto generated proxy file content
/// made from xsd.exe tool
/// </param>
/// <param name="schema">The XML schema</param>
/// <param name="language">The code language</param>
/// <param name="factoryGuid">The COM guid to set in the generated factory class </param>
/// <returns>Namespace contains the wrapper implementation code.</returns>
/// <remarks>
/// The given proxy file content must be in VB.NET or C# language, and .NET 2.0 compatible
/// </remarks>
public CodeNamespace CreateComWrapper(TextReader proxyFileCode, SupportedLanguage language, Guid factoryGuid, string schema)
{
m_factoryImpGuid = factoryGuid;
m_schemaObjectsLst.Clear();
m_schemaEnumLst.Clear();
if (proxyFileCode == null) throw new ArgumentNullException("proxyFileCode");
string proxyCode = proxyFileCode.ReadToEnd();
string rootClassName = GetRootElementClassName(new StringReader(proxyCode));
if (string.IsNullOrEmpty(rootClassName))
throw new ArgumentException("Failed locate the class that describes the XML root element in the code file", "proxyFile");
TextReader reader = new StringReader(proxyCode);
CodeNamespace ns = Parse(reader, language).Namespaces[0];
return TransformToWrapper(ns, schema, rootClassName);
}
/// <summary>
/// Returns the name of the root element class from
/// xsd.exe auto generated code.
/// </summary>
/// <param name="schemaCodeReader">Reader to xsd.exe auto generated code</param>
/// <returns>
/// The name of the root element class, or null if no root alement was detected
/// </returns>
private string GetRootElementClassName( TextReader xsdSchemaCodeReader )
{
string xsdCode = xsdSchemaCodeReader.ReadToEnd();
//finding close index to the class that is the owner of "XmlRootAttribute";
int index = xsdCode.IndexOf("XmlRootAttribute");
if( index < 0 ) return null;
index = xsdCode.IndexOf(" class ", index);
if( index < 0 ) return null;
string abcStr = "abcdefghijklmnopqrstuvwxyz";
int nameStartIndex = xsdCode.IndexOfAny(abcStr.ToCharArray(), index + " class ".Length);
int nameStartIndex2 = xsdCode.IndexOfAny(abcStr.ToUpper().ToCharArray(), index + " class ".Length);
if( nameStartIndex > 0 && nameStartIndex2 > 0 )
nameStartIndex = Math.Min(nameStartIndex, nameStartIndex2);
else
nameStartIndex = Math.Max(nameStartIndex, nameStartIndex2);
char[] stopChars = new char[]{' ',';',':'};
int nameLastIndex = xsdCode.IndexOfAny(stopChars, nameStartIndex);
string className = xsdCode.Substring(nameStartIndex, nameLastIndex - nameStartIndex);
return className;
}
/// <summary>
/// Writes the given code to a file
/// </summary>
/// <param name="code">The code to write</param>
/// <param name="language">The code language</param>
/// <param name="filePath">The output file path</param>
private void WriteCode( CodeNamespace code, SupportedLanguage language, string filePath)
{
string strLang = language == SupportedLanguage.CSharp ? "CSharp" : "VisualBasic";
CodeDomProvider cp = CodeDomProvider.CreateProvider(strLang);
CodeGeneratorOptions opt = new CodeGeneratorOptions();
opt.BlankLinesBetweenMembers = true;
opt.ElseOnClosing = false;
opt.VerbatimOrder = true;
StringBuilder stringBuilder = new StringBuilder(2000);
TextWriter sw = new StringWriter(stringBuilder); //new StreamWriter(filePath);
try
{
cp.GenerateCodeFromNamespace(code, sw, opt);
}
finally{
sw.Close();
}
switch (language)
{
case SupportedLanguage.CSharp:
stringBuilder.Replace("public class", "internal class");
stringBuilder.Replace("internal class XmlFactory", "public class XmlFactory");
break;
case SupportedLanguage.VBNet:
stringBuilder.Replace("Public Class", "Private Class");
stringBuilder.Replace("Private Class XmlFactory", "Public Class XmlFactory");
break;
}
File.WriteAllText(filePath, stringBuilder.ToString());
}
public void CompileCSharpCode( List<string> files, string outputFilePath, bool generateXmlDocFile)
{
CompilerParameters comparam = new CompilerParameters(new string[] { "mscorlib.dll" });
comparam.ReferencedAssemblies.Add("System.dll");
comparam.ReferencedAssemblies.Add("System.Xml.dll");
//Indicates Whether the compiler has to generate the output in //memory
comparam.GenerateInMemory = false;
//Indicates whether the output is an executable.
comparam.GenerateExecutable = false;
//provide the path where the generated assembly would be placed
comparam.OutputAssembly = outputFilePath;
comparam.IncludeDebugInformation = false;
comparam.TreatWarningsAsErrors = false;
if (generateXmlDocFile)
{
string xmlDocFilePath = Path.GetDirectoryName(outputFilePath) + Path.DirectorySeparatorChar + Path.GetFileNameWithoutExtension(outputFilePath) + ".xml";
comparam.CompilerOptions = "/doc:" + '"' + xmlDocFilePath +'"';
}
CodeDomProvider cp = CodeDomProvider.CreateProvider("CSharp");
CompilerResults compres = cp.CompileAssemblyFromFile(comparam, files.ToArray());
StringBuilder strBuild = new StringBuilder(250);
if (compres == null || compres.Errors.Count>0)
{
for (int i=0; i<compres.Errors.Count;i++)
strBuild.AppendLine(compres.Errors[i].ToString());
}
}
/// <summary>
/// Attempts to parse a code stream into <see cref="CodeCompileUnit"/>
/// </summary>
/// <param name="reader">Reader to read from the code to parse</param>
/// <param name="language">The programming language that the code is written at</param>
/// <returns><see cref="CodeCompileUnit"/> that contains the parsed code.</returns>
/// <exception cref="InvalidDataException">Thrown when parsing the code failed.</exception>
private CodeCompileUnit Parse(System.IO.TextReader reader, SupportedLanguage language)
{
IParser parser = ParserFactory.CreateParser(language, reader);
parser.Parse();
if (parser.Errors.Count > 0)
{
string errMsg = string.Format("{0} errors detected while parsing the code stream: \r\n{1}", parser.Errors.Count, parser.Errors.ErrorOutput);
throw new InvalidDataException(errMsg);
}
CodeDomVisitor visit = new CodeDomVisitor();
visit.VisitCompilationUnit(parser.CompilationUnit, null);
// Remove Unused Namespaces
for (int i = 0; i < visit.codeCompileUnit.Namespaces.Count; ++i)
{
if (visit.codeCompileUnit.Namespaces[i].Types.Count == 0)
visit.codeCompileUnit.Namespaces.RemoveAt(i);
}
return visit.codeCompileUnit;
}
/// <summary>
/// Accepts XML schema proxy code generated by xsd.exe tool
/// and returns new code with corresponding class wrappers
/// </summary>
/// <param name="proxyNs">XML schema proxy code generated by xsd.exe tool</param>
/// <param name="rootClassName">
/// The name of the class in the given code, that represent the root element in XML.
/// </param>
/// <param name="schema">The XML schema</param>
/// <returns>Code with corresponding wrappers</returns>
private CodeNamespace TransformToWrapper(CodeNamespace proxyNs, string schema, string rootClassName)
{
m_proxyNamespaceName = proxyNs.Name;
m_impNamespaceName = proxyNs.Name + ".ComXml";
m_InterfaceNamespaceName = proxyNs.Name + ".ComXml.Interfaces";
CodeNamespace ns = new CodeNamespace(m_impNamespaceName);
ns.Imports.Add(new CodeNamespaceImport(m_InterfaceNamespaceName));
CodeTypeDeclaration reader = CreateObjectReader("I" + rootClassName,schema, rootClassName);
CodeTypeDeclaration writer = CreateObjectWriter("I" + rootClassName, rootClassName);
CodeTypeDeclaration factory = CreateFactory(reader, writer, "IXmlValidator");
ns.Types.Add(factory);
ns.Types.AddRange( new CodeTypeDeclaration[] {reader, writer});
//storing the names of all the classes and enumerations
foreach (CodeTypeDeclaration type in proxyNs.Types)
{
if(type.IsClass)
m_schemaObjectsLst.Add(type.Name); //storing the names of all the classes
else if( type.IsEnum )
m_schemaEnumLst.Add(type.Name);
}
//Creating com wrappers
foreach( CodeTypeDeclaration type in proxyNs.Types )
{
if (!type.IsClass) continue;
AddXmlObjectExtractMthd(type, writer);
CodeTypeDeclaration createdwrapper = GenerateComWrapper(type);
ns.Types.Add(createdwrapper);
AddFactoryCreationMthd(factory, createdwrapper);
}
return ns;
}
/// <summary>
/// Creates method that converts XmlSchema Wrapper instance into basic Xml Proxy type
/// </summary>
/// <param name="mthdOwner">The type to add the method to.</param>
/// <param name="proxyType">The raw type to create extractor to.</param>
private void AddXmlObjectExtractMthd( CodeTypeDeclaration proxyType, CodeTypeDeclaration mthdOwner)
{
CodeMemberMethod convMethd = new CodeMemberMethod();
convMethd.Name = "GetProxyClass";
convMethd.ReturnType = new CodeTypeReference(proxyType.Name);
convMethd.Attributes = MemberAttributes.Private | MemberAttributes.Static;
CodeParameterDeclarationExpression mthdParam = new CodeParameterDeclarationExpression( "I" + proxyType.Name, "xmlObject" );
CodeArgumentReferenceExpression mthdArgRef = new CodeArgumentReferenceExpression(mthdParam.Name);
convMethd.Parameters.Add(mthdParam);
CodeVariableDeclarationStatement rawObjDecl =
new CodeVariableDeclarationStatement(proxyType.Name, "rawObj",
new CodeObjectCreateExpression(proxyType.Name));
CodeVariableReferenceExpression rawObjRef = new CodeVariableReferenceExpression(rawObjDecl.Name);
convMethd.Statements.Add(rawObjDecl);
mthdOwner.Members.Add(convMethd);
foreach (CodeTypeMember member in proxyType.Members)
{
if (member == null ) continue;
if( !( member is CodeMemberProperty )) continue;
CodeMemberProperty prop = member as CodeMemberProperty;
CodePropertyReferenceExpression rawPropRef = new CodePropertyReferenceExpression(rawObjRef, prop.Name);
CodePropertyReferenceExpression propRef = new CodePropertyReferenceExpression(mthdArgRef, prop.Name);
//If the property is enumeration, transform the enumeration before assignment
if (m_schemaEnumLst.Contains(prop.Type.BaseType))
{
CodeTypeOfExpression typeofEnumExp = new CodeTypeOfExpression( m_proxyNamespaceName + "." + prop.Type.BaseType);
CodeMethodInvokeExpression mthd = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(typeof(Enum)),
"Parse", typeofEnumExp,
new CodeMethodInvokeExpression(propRef, "ToString"));
convMethd.Statements.Add(new CodeAssignStatement(rawPropRef, new CodeCastExpression(typeofEnumExp.Type, mthd)));
continue;
}
//if the property is non complex type just copy the value
else if( ! m_schemaObjectsLst.Contains(prop.Type.BaseType) )
{
convMethd.Statements.Add( new CodeAssignStatement(rawPropRef,propRef) );
continue;
}
//by now surly a complex type:
if( prop.Type.ArrayRank == 0 )//non array type
{
CodeMethodInvokeExpression extractRawMemberMthd = new CodeMethodInvokeExpression(null, convMethd.Name, propRef);
convMethd.Statements.Add( new CodeAssignStatement(rawPropRef,extractRawMemberMthd) );
continue;
}
//by now surly a complex type array
CodeStatement copyStatement = CreateComplexArrayToRawArrayCopyCode(propRef, rawPropRef, prop.Type.BaseType, convMethd.Name);
convMethd.Statements.Add(copyStatement);
}
convMethd.Statements.Add(new CodeMethodReturnStatement(rawObjRef));
}
/// <summary>
/// Generated code that copies array of XmlSchema object wrapper class instances,
/// into array of raw xml proxy objects
/// </summary>
/// <param name="sourceArray">The array to copy from or null</param>
/// <param name="targetArray">The array to copy to</param>
/// <param name="arrayType">The type of the array to create</param>
/// <param name="rawTypeExtractionMthdName">Name of method that converts wrapper types into raw types.</param>
/// <returns>Collection of code statements</returns>
private CodeStatement CreateComplexArrayToRawArrayCopyCode( CodeExpression sourceArray, CodeExpression targetArray, string arrayType, string rawTypeExtractionMthdName)
{
CodeStatementCollection statements = new CodeStatementCollection();
CodePrimitiveExpression nullExp = new CodePrimitiveExpression(null);
CodeAssignStatement nullAsignCode = new CodeAssignStatement(targetArray, nullExp);
List<CodeStatement> copyingCode = new List<CodeStatement>();
CodePropertyReferenceExpression srcArrLengthProp =
new CodePropertyReferenceExpression(sourceArray, "Length");
CodeAssignStatement assignArr = new CodeAssignStatement(targetArray,
new CodeArrayCreateExpression(arrayType,srcArrLengthProp)
);
copyingCode.Add(assignArr);
CodeIterationStatement loopCode = CreateIterationStatement("i", srcArrLengthProp);
CodeAssignStatement objAssign = new CodeAssignStatement(
new CodeArrayIndexerExpression( targetArray, new CodeVariableReferenceExpression("i")),
new CodeMethodInvokeExpression(null, rawTypeExtractionMthdName,
new CodeArrayIndexerExpression(sourceArray,
new CodeVariableReferenceExpression("i")
)
)
);
loopCode.Statements.Add(objAssign);
copyingCode.Add( loopCode );
CodeBinaryOperatorExpression compToNull =
new CodeBinaryOperatorExpression (sourceArray,
CodeBinaryOperatorType.ValueEquality,
nullExp);
CodeConditionStatement condState =
new CodeConditionStatement(compToNull, new CodeStatement[] { nullAsignCode }, copyingCode.ToArray());
return condState;
}
/// <summary>
/// Creates factory with methods to create XML writer and XML writer
/// </summary>
/// <param name="reader">The object reader class</param>
/// <param name="writer">The object writer class</param>
/// <param name="validatorInterfactName">The name of the validation interface</param>
/// <returns>The factory that was created</returns>
private CodeTypeDeclaration CreateFactory(CodeTypeDeclaration reader, CodeTypeDeclaration writer, string validatorInterfactName)
{
CodeTypeDeclaration factory = new CodeTypeDeclaration("XmlFactory");
factory.IsClass = true;
factory.Attributes = MemberAttributes.Public;
factory.BaseTypes.Add("IXmlFactory");
factory.Comments.Add(new CodeCommentStatement("<summary>Factory class for constructing XML reading and writing objects according to a specific schema.</summary>", true));
AddComAttributes(factory);
CodeMemberMethod method = new CodeMemberMethod();
method.Name = "CreateWriter";
method.ReturnType = new CodeTypeReference( writer.BaseTypes[0].BaseType);
method.Attributes = MemberAttributes.Public;
method.Comments.Add(new CodeCommentStatement("<summary>Creates XML objects writer according to a specific schema.</summary>", true));
method.Comments.Add(new CodeCommentStatement("<returns>The created writer.</returns>", true));
CodeObjectCreateExpression create = new CodeObjectCreateExpression("XmlWriter");
method.Statements.Add(new CodeMethodReturnStatement(create));
factory.Members.Add(method);
method = new CodeMemberMethod();
method.Name = "CreateReader";
method.ReturnType = new CodeTypeReference(reader.BaseTypes[0].BaseType);
method.Attributes = MemberAttributes.Public;
method.Comments.Add(new CodeCommentStatement("<summary>Creates XML objects reader according to a specific schema.</summary>", true));
method.Comments.Add(new CodeCommentStatement("<returns>The created reader.</returns>", true));
create = new CodeObjectCreateExpression("XmlReader", new CodePrimitiveExpression(null));
method.Statements.Add(new CodeMethodReturnStatement(create));
factory.Members.Add(method);
method = new CodeMemberMethod();
method.Name = "CreateReader";
method.ReturnType = new CodeTypeReference(reader.BaseTypes[0].BaseType);
method.Parameters.Add(new CodeParameterDeclarationExpression(validatorInterfactName, "validationHandler"));
method.Attributes = MemberAttributes.Public;
method.Comments.Add(new CodeCommentStatement("<summary>Creates XML objects writer according to a specific schema with validation handler.</summary>", true));
method.Comments.Add(new CodeCommentStatement("<param name=\"validationHandler\">Validation events handler</param>", true));
method.Comments.Add(new CodeCommentStatement("<returns>The created reader.</returns>", true));
create = new CodeObjectCreateExpression("XmlReader", new CodeArgumentReferenceExpression("validationHandler"));
method.Statements.Add(new CodeMethodReturnStatement(create));
factory.Members.Add(method);
return factory;
}
/// <summary>
/// Add object creation method to a factory.
/// </summary>
/// <param name="factory">The factory to add the method to</param>
/// <param name="typeToCreate">The type of object that the method should return.</param>
private void AddFactoryCreationMthd( CodeTypeDeclaration factory, CodeTypeDeclaration typeToCreate)
{
//Remove the 'Wrapper' postfix from the type name
string typeName = typeToCreate.Name.Substring(0, typeToCreate.Name.LastIndexOf("Wrapper"));
CodeMemberMethod method = new CodeMemberMethod();
method.Name = "Create" + typeName;
method.ReturnType = new CodeTypeReference(typeToCreate.BaseTypes[0].BaseType);
method.Attributes = MemberAttributes.Public;
method.Comments.Add(new CodeCommentStatement("<summary>Creates and returns " + typeToCreate.Name + "</summary>", true));
method.Comments.Add(new CodeCommentStatement("<returns>The created "+ typeToCreate.Name +".</returns>", true));
CodeObjectCreateExpression create = new CodeObjectCreateExpression(typeToCreate.Name);
method.Statements.Add(new CodeMethodReturnStatement(create));
factory.Members.Add(method);
}
/// <summary>
/// Creates and returns XML reader objects that reads
/// objects from XML file or stream.
/// </summary>
/// <param name="readingTypeName">The name of the type that the reader should return.</param>
/// <param name="schmea">The XML schema</param>
/// <param name="schemaRootClassName">The name of the root element in the schema proxy code</param>
/// <returns>XML reader objects that reads XML stream according to schema</returns>
private CodeTypeDeclaration CreateObjectReader( string readingTypeName, string schmea, string schemaRootClassName)
{
CodeTypeDeclaration reader = new CodeTypeDeclaration("XmlReader");
reader.IsClass = true;
reader.Attributes = MemberAttributes.Public;
reader.BaseTypes.Add("IXmlReader");
AddComAttributes(reader);
//adding private members
CodeMemberField readerSettingsField = new CodeMemberField(typeof(System.Xml.XmlReaderSettings), "m_xmlReaderSettings");
readerSettingsField.Attributes = MemberAttributes.Private | MemberAttributes.Static;
reader.Members.Add(readerSettingsField);
CodeMemberField serializerField = new CodeMemberField(typeof(System.Xml.Serialization.XmlSerializer), "m_serializer");
serializerField.Attributes = MemberAttributes.Private | MemberAttributes.Static;
reader.Members.Add(serializerField);
CodeMemberField schemaField = new CodeMemberField(typeof(string), "m_schemaStr");
schemaField.Attributes = MemberAttributes.Private | MemberAttributes.Static;
schemaField.InitExpression = new CodePrimitiveExpression(schmea);
reader.Members.Add(schemaField);
CodeMemberField validatorField = new CodeMemberField("IXmlValidator", "m_validator");
validatorField.Attributes = MemberAttributes.Private;
reader.Members.Add(validatorField);
CodeConstructor ctor = new CodeConstructor();
ctor.Attributes = MemberAttributes.Assembly;
CodeParameterDeclarationExpression ctorArg = new CodeParameterDeclarationExpression("IXmlValidator", "validatorHandler");
ctor.Parameters.Add(ctorArg);
CodeThisReferenceExpression thisRef = new CodeThisReferenceExpression();
CodeFieldReferenceExpression validatorFieldRef = new CodeFieldReferenceExpression(thisRef, validatorField.Name);
CodeFieldReferenceExpression settingFieldRef = new CodeFieldReferenceExpression(null, "m_xmlReaderSettings");
ctor.Statements.Add(new CodeAssignStatement( validatorFieldRef,new CodeArgumentReferenceExpression(ctorArg.Name) ));
CodePrimitiveExpression nullExp = new CodePrimitiveExpression(null);
Type factoryType = typeof(System.Xml.Serialization.XmlSerializerFactory);
CodeVariableDeclarationStatement factVarDecl = new CodeVariableDeclarationStatement(factoryType, "fact", new CodeObjectCreateExpression(factoryType));
ctor.Statements.Add(factVarDecl);
CodeFieldReferenceExpression serializerRef = new CodeFieldReferenceExpression(null, serializerField.Name);
CodeMethodInvokeExpression createSerializerMthd = new CodeMethodInvokeExpression(new CodeVariableReferenceExpression(factVarDecl.Name),"CreateSerializer", new CodeTypeOfExpression(schemaRootClassName));
ctor.Statements.Add(new CodeAssignStatement(serializerRef, createSerializerMthd));
CodeDelegateCreateExpression validationDel = new CodeDelegateCreateExpression( new CodeTypeReference(typeof(System.Xml.Schema.ValidationEventHandler)), thisRef,"OnSchemaValidateXmlError" );
CodeMethodInvokeExpression scheamCreatMthd =
new CodeMethodInvokeExpression(
new CodeVariableReferenceExpression("System.Xml.Schema.XmlSchema"),
"Read", new CodeObjectCreateExpression(typeof(StringReader),
new CodeFieldReferenceExpression(null, schemaField.Name)),
validationDel);
CodeVariableDeclarationStatement schemVarDecl = new CodeVariableDeclarationStatement(typeof(System.Xml.Schema.XmlSchema), "schema", scheamCreatMthd);
ctor.Statements.Add(schemVarDecl);
CodeAssignStatement settingCreate = new CodeAssignStatement(settingFieldRef, new CodeObjectCreateExpression(typeof(System.Xml.XmlReaderSettings)));
ctor.Statements.Add(settingCreate);
CodeAssignStatement assign = new CodeAssignStatement(new CodePropertyReferenceExpression(settingFieldRef, "ValidationType"), new CodeVariableReferenceExpression("System.Xml.ValidationType.Schema"));
ctor.Statements.Add(assign);
CodeAttachEventStatement addDelegate = new CodeAttachEventStatement(settingFieldRef, "ValidationEventHandler", validationDel);
ctor.Statements.Add(addDelegate);
CodeMethodInvokeExpression addSchemaMthd = new CodeMethodInvokeExpression(new CodePropertyReferenceExpression(settingFieldRef, "Schemas"), "Add", new CodeVariableReferenceExpression(schemVarDecl.Name));
ctor.Statements.Add(addSchemaMthd);
reader.Members.Add(ctor);
//Adding validation method
CodeMemberMethod method = new CodeMemberMethod();
method.Name = "OnSchemaValidateXmlError";
method.Attributes = MemberAttributes.Private;
method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(object), "sender"));
method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(ValidationEventArgs), "e"));
CodeBinaryOperatorExpression compare = new CodeBinaryOperatorExpression(
validatorFieldRef, CodeBinaryOperatorType.ValueEquality, nullExp
);
method.Statements.Add(new CodeConditionStatement(compare, new CodeMethodReturnStatement()));
CodeVariableDeclarationStatement sevVarDecl = new CodeVariableDeclarationStatement("ValidationSeverity", "sev", new CodeVariableReferenceExpression("ValidationSeverity.Error"));
method.Statements.Add(sevVarDecl);
CodeVariableReferenceExpression sevVarRef = new CodeVariableReferenceExpression(sevVarDecl.Name);
CodePropertyReferenceExpression sevArgRef = new CodePropertyReferenceExpression( new CodeArgumentReferenceExpression(method.Parameters[1].Name), "Severity");
assign = new CodeAssignStatement(sevVarRef, new CodeVariableReferenceExpression("ValidationSeverity.Warning"));
compare = new CodeBinaryOperatorExpression(
sevArgRef , CodeBinaryOperatorType.ValueEquality, new CodeVariableReferenceExpression("System.Xml.Schema.XmlSeverityType.Warning")
);
method.Statements.Add(new CodeConditionStatement(compare, assign));
CodePropertyReferenceExpression validateMsgArg = new CodePropertyReferenceExpression(new CodeArgumentReferenceExpression(method.Parameters[1].Name), "Message");
method.Statements.Add(new CodeMethodInvokeExpression(validatorFieldRef, "OnXmlSchemaValidationError", validateMsgArg, sevVarRef));
reader.Members.Add(method);
//Adding file reading method
method = new CodeMemberMethod();
method.Name = "ReadFromFile";
method.Attributes = MemberAttributes.Public;
method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "xmlFilePath"));
method.ReturnType = new CodeTypeReference(readingTypeName);
CodeMethodInvokeExpression readerCreatMthd = new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("System.Xml.XmlReader"), "Create", new CodeArgumentReferenceExpression(method.Parameters[0].Name), settingFieldRef);
CodeVariableDeclarationStatement readerVarDecl = new CodeVariableDeclarationStatement(typeof(System.Xml.XmlReader), "reader", readerCreatMthd);
method.Statements.Add(readerVarDecl);
CodeVariableDeclarationStatement compVarDecl = new CodeVariableDeclarationStatement(schemaRootClassName, "value", nullExp);
method.Statements.Add(compVarDecl);
CodeVariableReferenceExpression compVarRef = new CodeVariableReferenceExpression(compVarDecl.Name);
CodeVariableReferenceExpression readerVarRef = new CodeVariableReferenceExpression(readerVarDecl.Name);
CodeMethodInvokeExpression readMthd = new CodeMethodInvokeExpression(readerVarRef, "Read");
CodeMethodInvokeExpression serialMthd = new CodeMethodInvokeExpression(serializerRef, "Deserialize", readerVarRef);
assign = new CodeAssignStatement(compVarRef, new CodeCastExpression(schemaRootClassName, serialMthd));
method.Statements.Add(new CodeConditionStatement(readMthd, assign));
method.Statements.Add(new CodeMethodInvokeExpression(readerVarRef, "Close"));
method.Statements.Add(new CodeConditionStatement(new CodeBinaryOperatorExpression(compVarRef, CodeBinaryOperatorType.ValueEquality, nullExp), new CodeMethodReturnStatement(nullExp)));
method.Statements.Add(new CodeMethodReturnStatement(new CodeObjectCreateExpression(schemaRootClassName+ "Wrapper" , compVarRef)));
reader.Members.Add(method);
//Add stream reading method
method = new CodeMemberMethod();
method.Name = "ReadFromStream";
method.Attributes = MemberAttributes.Public;
method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "xmlStream"));
method.ReturnType = new CodeTypeReference(readingTypeName);
CodeObjectCreateExpression createStrReader = new CodeObjectCreateExpression(typeof(System.IO.StringReader), new CodeArgumentReferenceExpression(method.Parameters[0].Name));
readerCreatMthd = new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("System.Xml.XmlReader"), "Create",createStrReader , settingFieldRef);
readerVarDecl = new CodeVariableDeclarationStatement(typeof(System.Xml.XmlReader), "reader", readerCreatMthd);
method.Statements.Add(readerVarDecl);
method.Statements.Add(compVarDecl);
method.Statements.Add(new CodeConditionStatement(readMthd, assign));
method.Statements.Add(new CodeMethodInvokeExpression(readerVarRef, "Close"));
method.Statements.Add(new CodeConditionStatement(new CodeBinaryOperatorExpression(compVarRef, CodeBinaryOperatorType.ValueEquality, nullExp), new CodeMethodReturnStatement(nullExp)));
method.Statements.Add(new CodeMethodReturnStatement(new CodeObjectCreateExpression(schemaRootClassName + "Wrapper", compVarRef)));
reader.Members.Add(method);
//Adding properties:
CodeMemberProperty prop = new CodeMemberProperty();
prop.Name = "HasValidtionHandler";
prop.Type = new CodeTypeReference(typeof(bool));
prop.Attributes = MemberAttributes.Public;
prop.GetStatements.Add(new CodeMethodReturnStatement(new CodeBinaryOperatorExpression(nullExp, CodeBinaryOperatorType.IdentityInequality, validatorFieldRef)));
reader.Members.Add(prop);
prop = new CodeMemberProperty();
prop.Name = "Schema";
prop.Type = new CodeTypeReference(typeof(string));
prop.Attributes = MemberAttributes.Public;
prop.GetStatements.Add( new CodeMethodReturnStatement( new CodeFieldReferenceExpression(null, schemaField.Name)));
reader.Members.Add(prop);
return reader;
}
/// <summary>
/// Creates and returns XML writer objects that writes
/// objects to XML file or stream.
/// </summary>
/// <param name="readingTypeName">The name of the type that the writer should return.</param>
/// <param name="schemaRootClassName">The name of the root element in the schema proxy code</param>
/// <returns>XML writer objects that writes XML stream according schema</returns>
private CodeTypeDeclaration CreateObjectWriter(string WritingTypeName, string schemaRootClassName)
{
CodeTypeDeclaration writer = new CodeTypeDeclaration("XmlWriter");
writer.IsInterface = false;
writer.Attributes = MemberAttributes.Public;
writer.BaseTypes.Add("IXmlWriter");
AddComAttributes(writer);
CodeMemberField serializerField = new CodeMemberField(typeof(System.Xml.Serialization.XmlSerializer), "m_serializer");
serializerField.Attributes = MemberAttributes.Private | MemberAttributes.Static;
writer.Members.Add(serializerField);
CodeConstructor ctor = new CodeConstructor();
ctor.Attributes = MemberAttributes.Assembly;
Type factoryType = typeof(System.Xml.Serialization.XmlSerializerFactory);
CodeVariableDeclarationStatement factVarDecl = new CodeVariableDeclarationStatement(factoryType, "fact", new CodeObjectCreateExpression(factoryType));
ctor.Statements.Add(factVarDecl);
CodeThisReferenceExpression thisRef = new CodeThisReferenceExpression();
CodeFieldReferenceExpression serializerRef = new CodeFieldReferenceExpression(null, serializerField.Name);
CodeMethodInvokeExpression createSerializerMthd = new CodeMethodInvokeExpression(new CodeVariableReferenceExpression(factVarDecl.Name), "CreateSerializer", new CodeTypeOfExpression(schemaRootClassName));
ctor.Statements.Add(new CodeAssignStatement(serializerRef, createSerializerMthd));
writer.Members.Add(ctor);
CodeMemberMethod method = new CodeMemberMethod();
method.Name = "WriteToFile";
method.Attributes = MemberAttributes.Public;
method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "outXmlFilePath"));
method.Parameters.Add(new CodeParameterDeclarationExpression( WritingTypeName, "objectToWrite"));
CodeObjectCreateExpression fileStreamCreate =
new CodeObjectCreateExpression(typeof(FileStream),
new CodeArgumentReferenceExpression(method.Parameters[0].Name),
new CodeVariableReferenceExpression("System.IO.FileMode.Create"),
new CodeVariableReferenceExpression("System.IO.FileAccess.Write")
);
CodeVariableDeclarationStatement streamVarDecl = new CodeVariableDeclarationStatement(typeof(Stream),
"stream", fileStreamCreate);
CodeVariableReferenceExpression streamVarRef = new CodeVariableReferenceExpression(streamVarDecl.Name);
method.Statements.Add(streamVarDecl);
CodeMethodInvokeExpression callRawObjMthd = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(writer.Name), "GetProxyClass", new CodeArgumentReferenceExpression(method.Parameters[1].Name));
CodeVariableDeclarationStatement rawObjVarDecl = new CodeVariableDeclarationStatement(schemaRootClassName, "rawObj", callRawObjMthd);
method.Statements.Add(rawObjVarDecl);
CodeMethodInvokeExpression serialMthd = new CodeMethodInvokeExpression(serializerRef, "Serialize", streamVarRef, new CodeVariableReferenceExpression(rawObjVarDecl.Name));
method.Statements.Add(serialMthd);
method.Statements.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression(streamVarDecl.Name), "Close"));
writer.Members.Add(method);
method = new CodeMemberMethod();
method.Name = "WriteToStream";
method.Attributes = MemberAttributes.Public;
method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "outXmlStream"));
method.Parameters[0].Direction = FieldDirection.Out;
method.Parameters.Add(new CodeParameterDeclarationExpression(WritingTypeName, "objectToWrite"));
CodeObjectCreateExpression strBuildCreate= new CodeObjectCreateExpression(typeof(StringBuilder), new CodePrimitiveExpression(2000) );
CodeVariableDeclarationStatement strBuildVarDecl = new CodeVariableDeclarationStatement(typeof(StringBuilder), "str", strBuildCreate);
CodeVariableReferenceExpression strBuildVarRef = new CodeVariableReferenceExpression(strBuildVarDecl.Name);
method.Statements.Add(strBuildVarDecl);
CodeObjectCreateExpression writerCreate = new CodeObjectCreateExpression(typeof(StringWriter), strBuildVarRef);
CodeVariableDeclarationStatement writerVarDecl = new CodeVariableDeclarationStatement(typeof(StringWriter), "writer", writerCreate);
CodeVariableReferenceExpression writerVarRef = new CodeVariableReferenceExpression(writerVarDecl.Name);
method.Statements.Add(writerVarDecl);
callRawObjMthd = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(writer.Name), "GetProxyClass", new CodeArgumentReferenceExpression(method.Parameters[1].Name));
rawObjVarDecl = new CodeVariableDeclarationStatement(schemaRootClassName, "rawObj", callRawObjMthd);
method.Statements.Add(rawObjVarDecl);
serialMthd = new CodeMethodInvokeExpression(serializerRef, "Serialize", writerVarRef, new CodeVariableReferenceExpression(rawObjVarDecl.Name));
method.Statements.Add(serialMthd);
method.Statements.Add(new CodeMethodInvokeExpression(writerVarRef, "Close"));
method.Statements.Add(new CodeAssignStatement(
new CodeArgumentReferenceExpression(method.Parameters[0].Name),
new CodeMethodInvokeExpression(strBuildVarRef, "ToString"))
);
writer.Members.Add(method);
return writer;
}
/// <summary>
/// Generates COM wrapper type form the given class type.
/// </summary>
/// <param name="classType">
/// XML schema proxy class type generated by xsd.exe tool
/// </param>
/// <returns>Interface type</returns>
/// <remarks>
/// This method assumes that the type given is generated from xsd.exe tool
/// thus, have a public get & set property for each data member
/// </remarks>
private CodeTypeDeclaration GenerateComWrapper(CodeTypeDeclaration classType)
{
CodeTypeDeclaration wrapperType = new CodeTypeDeclaration( classType.Name + "Wrapper");
wrapperType.IsClass = true;
wrapperType.Attributes = MemberAttributes.Public;
wrapperType.BaseTypes.Add("I" + classType.Name);
AddComAttributes(wrapperType);
//Creating c'tors
CodeConstructor ctor = new CodeConstructor();//parameterless
ctor.Attributes = MemberAttributes.Assembly;
wrapperType.Members.Add(ctor);
//collecting snippets of code for other c'tors
CodeStatementCollection codeStatements = new CodeStatementCollection();
//Duplicate the properties & fields
foreach (CodeTypeMember member in classType.Members)
{
if (member == null ) continue;
if( member is CodeMemberField )
{
CodeMemberField field = member as CodeMemberField;
if (m_schemaObjectsLst.Contains(field.Type.BaseType))
{
CodeTypeReference typeRef = field.Type;
field.Type = new CodeTypeReference(typeRef.BaseType + "Wrapper");
field.Type.ArrayRank = typeRef.ArrayRank;
field.Type.Options = typeRef.Options;
}
}
else if( member is CodeMemberProperty )
{
CodeMemberProperty prop = member as CodeMemberProperty;
CodeArgumentReferenceExpression arg = new CodeArgumentReferenceExpression("wrappedObject");//C'tor argument reference
CodePropertyReferenceExpression argProp = new CodePropertyReferenceExpression(arg,prop.Name);
CodePropertyReferenceExpression localProp = new CodePropertyReferenceExpression( new CodeThisReferenceExpression(), prop.Name);
if (m_schemaObjectsLst.Contains(prop.Type.BaseType))//none primitive type( wrapper is needed )
{
CodeTypeReference orgType = prop.Type;
prop.Type = new CodeTypeReference("I" + orgType.BaseType );
prop.Type.ArrayRank = orgType.ArrayRank;
if (orgType.ArrayRank < 1)
{// non array
CodeAssignStatement setCode = prop.SetStatements[0] as CodeAssignStatement;
setCode.Right = new CodeObjectCreateExpression(orgType.BaseType + "Wrapper", new CodeVariableReferenceExpression("value"));
codeStatements.Add(new CodeAssignStatement(localProp, new CodeObjectCreateExpression(orgType.BaseType + "Wrapper", argProp)));
}
else{//array of complex type
CodeAssignStatement orgSetCode = prop.SetStatements[0] as CodeAssignStatement;
prop.SetStatements.Clear();
prop.SetStatements.AddRange(
CreateArrayCopyCode(orgSetCode.Right, orgSetCode.Left, new CodeTypeReference(orgType.BaseType + "Wrapper"))
);
//The ctor will need copying as well:
codeStatements.AddRange(
CreateArrayCopyCode(new CodePropertyReferenceExpression(new CodeArgumentReferenceExpression("wrappedObject"), prop.Name), orgSetCode.Left, new CodeTypeReference(orgType.BaseType + "Wrapper"))
);
}
}
else if(m_schemaEnumLst.Contains(prop.Type.BaseType))//is enumeration type
{
CodeTypeOfExpression typeofEnumExp = new CodeTypeOfExpression(prop.Type);
CodeMethodInvokeExpression mthd = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(typeof(Enum)),
"Parse", typeofEnumExp,
new CodeMethodInvokeExpression(argProp, "ToString"));
codeStatements.Add(new CodeAssignStatement(localProp, new CodeCastExpression(typeofEnumExp.Type, mthd)));
}
else
codeStatements.Add( new CodeAssignStatement(localProp, argProp));
}
wrapperType.Members.Add( member);
}
//C'tor with Interface as parameter
ctor = new CodeConstructor();
ctor.Attributes = MemberAttributes.Assembly;
ctor.Parameters.Add(new CodeParameterDeclarationExpression("I" + classType.Name, "wrappedObject"));
ctor.Statements.AddRange(codeStatements);
wrapperType.Members.Add(ctor);
//C'tor with raw schema object as parameter
ctor = new CodeConstructor();
ctor.Attributes = MemberAttributes.Assembly;
ctor.Parameters.Add(new CodeParameterDeclarationExpression(classType.Name, "wrappedObject"));
ctor.Statements.AddRange(codeStatements);
wrapperType.Members.Add(ctor);
return wrapperType;
}
/// <summary>
/// Adds COM attributes to the given code type(Class or Interface)
/// </summary>
/// <param name="type">The class or interface to add COM attributes to</param>
private void AddComAttributes(CodeTypeDeclaration type)
{
if (!type.IsInterface && !type.IsClass)
throw new ArgumentException("Cannot add COM attributes to non class or interface", "type");
CodeAttributeDeclaration guidAttr = new CodeAttributeDeclaration(
new CodeTypeReference(typeof(System.Runtime.InteropServices.GuidAttribute))
);
CodeExpression exp = null;
if (type.BaseTypes[0].BaseType != "IXmlFactory")
exp = new CodePrimitiveExpression(Guid.NewGuid().ToString().ToUpper());
else
exp = new CodePrimitiveExpression(m_factoryImpGuid.ToString());
guidAttr.Arguments.Add(new CodeAttributeArgument(exp));
type.CustomAttributes.Add(guidAttr);
CodeAttributeDeclaration comVisibleAttr = new CodeAttributeDeclaration(
new CodeTypeReference(typeof(System.Runtime.InteropServices.ComVisibleAttribute))
);
if (type.BaseTypes[0].BaseType == "IXmlFactory")
exp = new CodePrimitiveExpression(true);
else
exp = new CodePrimitiveExpression(false);
comVisibleAttr.Arguments.Add(new CodeAttributeArgument(exp));
type.CustomAttributes.Add(comVisibleAttr);
if (type.IsInterface)
{
CodeAttributeDeclaration comInterfaceTypeAttr = new CodeAttributeDeclaration(
new CodeTypeReference(typeof(System.Runtime.InteropServices.InterfaceTypeAttribute))
);
CodePropertyReferenceExpression enumRef = new CodePropertyReferenceExpression(
new CodeVariableReferenceExpression("System.Runtime.InteropServices.ComInterfaceType"), "InterfaceIsIUnknown"
);
comInterfaceTypeAttr.Arguments.Add(new CodeAttributeArgument(enumRef));
type.CustomAttributes.Add(comInterfaceTypeAttr);
}
}
/// <summary>
/// Generated array copying code
/// </summary>
/// <param name="sourceArray">The array to copy from or null</param>
/// <param name="targetArray">The array to copy to</param>
/// <param name="arrayType">The type elements in the target array</param>
/// <returns>Collection of code statements</returns>
private CodeStatementCollection CreateArrayCopyCode( CodeExpression sourceArray, CodeExpression targetArray, CodeTypeReference arrayType)
{
CodeStatementCollection statements = new CodeStatementCollection();
CodePrimitiveExpression nullExp = new CodePrimitiveExpression(null);
CodeAssignStatement nullAsignCode = new CodeAssignStatement(targetArray, nullExp);
List<CodeStatement> copyingCode = new List<CodeStatement>();
CodePropertyReferenceExpression srcArrLengthProp =
new CodePropertyReferenceExpression(sourceArray, "Length");
CodeAssignStatement assignArr = new CodeAssignStatement(targetArray,
new CodeArrayCreateExpression(arrayType,srcArrLengthProp)
);
copyingCode.Add(assignArr);
CodeIterationStatement loopCode = CreateIterationStatement("i", srcArrLengthProp);
CodeAssignStatement objAssign = new CodeAssignStatement(
new CodeArrayIndexerExpression( targetArray, new CodeVariableReferenceExpression("i")),
new CodeObjectCreateExpression(arrayType,
new CodeArrayIndexerExpression(sourceArray,
new CodeVariableReferenceExpression("i")
)
)
);
loopCode.Statements.Add(objAssign);
copyingCode.Add( loopCode );
CodeBinaryOperatorExpression compToNull =
new CodeBinaryOperatorExpression (sourceArray,
CodeBinaryOperatorType.ValueEquality,
nullExp);
CodeConditionStatement condState =
new CodeConditionStatement(compToNull, new CodeStatement[] { nullAsignCode }, copyingCode.ToArray());
return new CodeStatementCollection(new CodeStatement[] { condState });
}
/// <summary>
/// Creates loop code expression with integer indexer with the given name that will iterate specific amount of times.
/// </summary>
/// <param name="interatorIndexVarName">The name of the iteration integer variable(will be set to zero)</param>
/// <param name="iterationCount">Code expression that specifies the number of iteration</param>
/// <remarks>The iterator is zero based indexer.</remarks>
/// <returns>Iteration code with no inner statements.</returns>
private CodeIterationStatement CreateIterationStatement(string interatorIndexVarName, CodeExpression iterationCount)
{
CodeVariableDeclarationStatement iterVar = new CodeVariableDeclarationStatement(typeof(int), interatorIndexVarName, new CodePrimitiveExpression(0));
return new CodeIterationStatement(
//new CodeAssignStatement(new CodeVariableReferenceExpression("testInt"), new CodePrimitiveExpression(1)),
iterVar,
new CodeBinaryOperatorExpression(new CodeVariableReferenceExpression(interatorIndexVarName),
CodeBinaryOperatorType.LessThan, iterationCount),
new CodeAssignStatement(new CodeVariableReferenceExpression(interatorIndexVarName), new CodeBinaryOperatorExpression(
new CodeVariableReferenceExpression(interatorIndexVarName), CodeBinaryOperatorType.Add, new CodePrimitiveExpression(1))
));
}
}
}