/// <summary> /// Implements the IVsSingleFileGenerator.Generate method. /// Executes the transformation and returns the newly generated output file, whenever a custom tool is loaded, or the input file is saved /// </summary> /// <param name="wszInputFilePath">The full path of the input file. May be a null reference (Nothing in Visual Basic) in future releases of Visual Studio, so generators should not rely on this value</param> /// <param name="bstrInputFileContents">The contents of the input file. This is either a UNICODE BSTR (if the input file is text) or a binary BSTR (if the input file is binary). If the input file is a text file, the project system automatically converts the BSTR to UNICODE</param> /// <param name="wszDefaultNamespace">This parameter is meaningful only for custom tools that generate code. It represents the namespace into which the generated code will be placed. If the parameter is not a null reference (Nothing in Visual Basic) and not empty, the custom tool can use the following syntax to enclose the generated code</param> /// <param name="rgbOutputFileContents">[out] Returns an array of bytes to be written to the generated file. You must include UNICODE or UTF-8 signature bytes in the returned byte array, as this is a raw stream. The memory for rgbOutputFileContents must be allocated using the .NET Framework call, System.Runtime.InteropServices.AllocCoTaskMem, or the equivalent Win32 system call, CoTaskMemAlloc. The project system is responsible for freeing this memory</param> /// <param name="pcbOutput">[out] Returns the count of bytes in the rgbOutputFileContent array</param> /// <param name="pGenerateProgress">A reference to the IVsGeneratorProgress interface through which the generator can report its progress to the project system</param> /// <returns>If the method succeeds, it returns S_OK. If it fails, it returns E_FAIL</returns> int IVsSingleFileGenerator.Generate(string wszInputFilePath, string bstrInputFileContents, string wszDefaultNamespace, IntPtr[] rgbOutputFileContents, out uint pcbOutput, IVsGeneratorProgress pGenerateProgress) { if (bstrInputFileContents == null) { debugOut.Flush(); throw new ArgumentNullException(bstrInputFileContents); } if (wszInputFilePath == null) { throw new ArgumentNullException(bstrInputFileContents); } string InputFilePath = wszInputFilePath; string FileNameSpace = wszDefaultNamespace; try { // class name should always the same as output file name string className = Path.GetFileNameWithoutExtension(InputFilePath); SchemaLoader typeLoader = new SchemaLoader(); typeLoader.SchemaResolver = new LocalXmlUrlResolver(Path.GetDirectoryName(InputFilePath)); typeLoader.Load(new MemoryStream(Encoding.UTF8.GetBytes(bstrInputFileContents))); string[] fakeArgs = { "DomGenVS", Path.GetFileName(InputFilePath), className + ".cs", className, FileNameSpace, "-enums" }; string bodyString = SchemaGen.Generate(typeLoader, "", FileNameSpace, className, fakeArgs); if (string.IsNullOrEmpty(bodyString)) { rgbOutputFileContents = null; pcbOutput = 0; return(VSConstants.E_FAIL); } else { // Return memory must be allocated by AllocCoTaskMem var bytes = Encoding.UTF8.GetBytes(bodyString); int outputLength = bytes.Length; rgbOutputFileContents[0] = Marshal.AllocCoTaskMem(outputLength); Marshal.Copy(bytes, 0, rgbOutputFileContents[0], outputLength); pcbOutput = (uint)outputLength; return(VSConstants.S_OK); } } catch (Exception exp) { rgbOutputFileContents = null; pcbOutput = 0; DebugOutput.ShowMessage("Error while compiling: " + exp.Message); return(VSConstants.E_FAIL); } }
private static void GenerateEnums(StringBuilder sb, SchemaLoader typeLoader, XmlQualifiedName[] namespaces, Dictionary <string, string> domNodeTypeToClassName, Dictionary <string, string> classNameToDomNodeType) { // Temp code // Currently the only way I can see to find out which strings are enum type // is to search for the StringEnumRule on the AttributeType // If this exists then use reflection to access the values list // This could and should be done in some nicer way in the future! System.Reflection.FieldInfo fInfo = typeof(StringEnumRule).GetField("m_values", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); if (fInfo != null) { var enumAttributeTypes = new HashSet <AttributeType>(); foreach (AttributeType attributeType in typeLoader.GetAttributeTypes()) { StringEnumRule rule = attributeType.Rules.FirstOrDefault(x => x is StringEnumRule) as StringEnumRule; if (rule != null) { if (enumAttributeTypes.Add(attributeType)) { string[] values = fInfo.GetValue(rule) as string[]; if (values != null) { string enumTypeName = GetClassName(namespaces, attributeType, domNodeTypeToClassName, classNameToDomNodeType); WriteLine(sb, ""); WriteLine(sb, " public enum {0}", enumTypeName); WriteLine(sb, " {{"); foreach (string value in values) { WriteLine(sb, " " + value + ","); } if (values.Length > 0) { sb.Length = sb.Length - 1; } WriteLine(sb, " }}\r"); } } } } } }
/// <summary> /// Generates the Schema CSharp class</summary> /// <param name="typeLoader">Type loader with loaded type and annotation information</param> /// <param name="schemaNamespace">Target namespace of the schema</param> /// <param name="codeNamespace">Namespace of the class ot be generated</param> /// <param name="className">Name of the class to be generated</param> /// <param name="args">Commandline arguments</param> /// <returns>CSharp Schema class as string</returns> public static string Generate(SchemaLoader typeLoader, string schemaNamespace, string codeNamespace, string className, string[] args) { bool generateAdapters = false; bool annotatedOnly = false; bool generateEnums = false; for (int i = 4; i < args.Length; i++) { string arg = args[i]; if (arg == "-a" || arg == "-adapters") { generateAdapters = true; } else if (arg == "-annotatedOnly") { annotatedOnly = true; } else if (arg == "-enums") { generateEnums = true; } } XmlSchemaTypeCollection typeCollection = GetTypeCollection(typeLoader, schemaNamespace); string targetNamespace = typeCollection.TargetNamespace; XmlQualifiedName[] namespaces = typeCollection.Namespaces; List <DomNodeType> nodeTypes = new List <DomNodeType>(typeCollection.GetNodeTypes()); // Append additional DomNodeTypes from other namespaces, for example if the xs:import was used. // Don't include the built-in "anyType". // The reason to append is that if there is a conflict with the imported types, priority should // be given to the types defined in the importing schema file. foreach (XmlQualifiedName knownNamespace in namespaces) { if (knownNamespace.Namespace != targetNamespace && knownNamespace.Namespace != "http://www.w3.org/2001/XMLSchema") { nodeTypes.AddRange(typeCollection.GetNodeTypes(knownNamespace.Namespace)); } } List <ChildInfo> rootElements = new List <ChildInfo>(typeCollection.GetRootElements()); StringBuilder sb = new StringBuilder(); GenerateFileProlog(codeNamespace, args, sb); WriteLine(sb, " public static class {0}", className); WriteLine(sb, " {{"); WriteLine(sb, " public const string NS = \"{0}\";", targetNamespace); WriteLine(sb, ""); WriteLine(sb, " public static void Initialize(XmlSchemaTypeCollection typeCollection)"); WriteLine(sb, " {{"); WriteLine(sb, " Initialize((ns,name)=>typeCollection.GetNodeType(ns,name),"); WriteLine(sb, " (ns,name)=>typeCollection.GetRootElement(ns,name));"); WriteLine(sb, " }}"); WriteLine(sb, ""); WriteLine(sb, " public static void Initialize(IDictionary<string, XmlSchemaTypeCollection> typeCollections)"); WriteLine(sb, " {{"); WriteLine(sb, " Initialize((ns,name)=>typeCollections[ns].GetNodeType(name),"); WriteLine(sb, " (ns,name)=>typeCollections[ns].GetRootElement(name));"); WriteLine(sb, " }}"); WriteLine(sb, ""); WriteLine(sb, " private static void Initialize(Func<string, string, DomNodeType> getNodeType, Func<string, string, ChildInfo> getRootElement)"); WriteLine(sb, " {{"); StringBuilder fieldSb = new StringBuilder(); Dictionary <string, string> domNodeTypeToClassName = new Dictionary <string, string>(nodeTypes.Count); Dictionary <string, string> classNameToDomNodeType = new Dictionary <string, string>(nodeTypes.Count); foreach (DomNodeType nodeType in nodeTypes) { NodeAnnotationInfo annotationInfo = new NodeAnnotationInfo(); typeLoader.DomGenAnnotations.TryGetValue(nodeType.Name, out annotationInfo); // Determine if the type should be included or skipped // If the -annotatedOnly argument is set: types will be included // ONLY IF <sce.domgen include="true"> is defined for this type, all other types will be skipped // If -annotatedOnly is NOT set: types will be included by default // UNLESS <sce.domgen include="false" is explicitly set for this type bool include = !annotatedOnly || annotationInfo.DomgenInclude; if (!include) { continue; // skip this type if it's not to be included } string ns = codeNamespace; if (ns == null) { ns = targetNamespace; } string typeName = GetClassName(namespaces, nodeType, domNodeTypeToClassName, classNameToDomNodeType); GenerateInitializers(nodeType, typeName, sb); WriteLine(sb, ""); WriteLine(fieldSb, ""); GenerateFields(nodeType, typeName, fieldSb); } if (generateEnums) { GenerateEnums(fieldSb, typeLoader, namespaces, domNodeTypeToClassName, classNameToDomNodeType); } foreach (ChildInfo rootElement in rootElements) { string fieldName = rootElement.Name + "RootElement"; WriteLine(fieldSb, ""); WriteLine(fieldSb, " public static ChildInfo {0};", fieldName); WriteLine(sb, " {0} = getRootElement(NS, \"{1}\");", fieldName, rootElement.Name); } WriteLine(sb, " }}"); sb.Append(fieldSb.ToString()); WriteLine(sb, " }}"); if (generateAdapters) { WriteLine(sb, ""); foreach (DomNodeType nodeType in nodeTypes) { string modifiers = (nodeType.IsAbstract) ? "abstract " : ""; string adapterClassName = GetClassName(nodeType, domNodeTypeToClassName); string adapterBaseClassName = "DomNodeAdapter"; if (nodeType.BaseType != DomNodeType.BaseOfAllTypes) { adapterBaseClassName = GetClassName(nodeType.BaseType, domNodeTypeToClassName); } WriteLine(sb, " public {0}partial class {1} : {2}", modifiers, adapterClassName, adapterBaseClassName); WriteLine(sb, " {{"); foreach (AttributeInfo attrInfo in nodeType.Attributes) { if (attrInfo.DefiningType != nodeType) { continue; } string typeName = attrInfo.Type.Type.ToString(); typeName = s_attributeTypes[typeName]; string attrName = attrInfo.Name; string propertyName = attrName; if (s_cSharpKeywords.Contains(propertyName)) { propertyName = "@" + propertyName; } WriteLine(sb, " public {0} {1}", typeName, propertyName); WriteLine(sb, " {{"); string attrInfoName = className + "." + adapterClassName + "." + attrName + "Attribute"; WriteLine(sb, " get {{ return GetAttribute<{0}>({1}); }}", typeName, attrInfoName); WriteLine(sb, " set {{ SetAttribute({0}, value); }}", attrInfoName); WriteLine(sb, " }}"); } foreach (ChildInfo childInfo in nodeType.Children) { if (childInfo.DefiningType != nodeType) { continue; } string childName = childInfo.Name; string propertyName = childName; string typeName = GetClassName(childInfo.Type, domNodeTypeToClassName); string childInfoName = className + "." + adapterClassName + "." + childName + "Child"; if (childInfo.IsList) { WriteLine(sb, " public IList<{0}> {1}", typeName, propertyName); WriteLine(sb, " {{"); WriteLine(sb, " get {{ return GetChildList<{0}>({1}); }}", typeName, childInfoName); WriteLine(sb, " }}"); } else { WriteLine(sb, " public {0} {1}", typeName, propertyName); WriteLine(sb, " {{"); WriteLine(sb, " get {{ return GetChild<{0}>({1}); }}", typeName, childInfoName); WriteLine(sb, " set {{ SetChild({0}, value); }}", childInfoName); WriteLine(sb, " }}"); } } WriteLine(sb, " }}"); WriteLine(sb, ""); } } GenerateFileEpilog(sb); return(sb.ToString()); }