// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; using System.Xml; using System.Xml.Schema; using Tools.DotNETCommon; using UnrealBuildTool; namespace AutomationTool { /// /// Information about a parameter to a task /// [DebuggerDisplay("{Name}")] class ScriptTaskParameter { /// /// Name of this parameter /// public string Name; /// /// Information about this field /// public FieldInfo FieldInfo; /// /// The type for values assigned to this field /// public Type ValueType; /// /// The ICollection interface for this type /// public Type CollectionType; /// /// Validation type for this field /// public TaskParameterValidationType ValidationType; /// /// Whether this parameter is optional /// public bool bOptional; /// /// Constructor /// public ScriptTaskParameter(string InName, FieldInfo InFieldInfo, TaskParameterValidationType InValidationType, bool bInOptional) { Name = InName; FieldInfo = InFieldInfo; ValueType = FieldInfo.FieldType; ValidationType = InValidationType; bOptional = bInOptional; if (ValueType.IsGenericType && ValueType.GetGenericTypeDefinition() == typeof(Nullable<>)) { ValueType = ValueType.GetGenericArguments()[0]; bOptional = true; } if(ValueType.IsClass) { foreach(Type InterfaceType in ValueType.GetInterfaces()) { if(InterfaceType.IsGenericType) { Type GenericInterfaceType = InterfaceType.GetGenericTypeDefinition(); if(GenericInterfaceType == typeof(ICollection<>)) { CollectionType = InterfaceType; ValueType = InterfaceType.GetGenericArguments()[0]; } } } } } } /// /// Helper class to serialize a task from an xml element /// [DebuggerDisplay("{Name}")] class ScriptTask { /// /// Name of this task /// public string Name; /// /// Type of the task to construct with this info /// public Type TaskClass; /// /// Type to construct with the parsed parameters /// public Type ParametersClass; /// /// Mapping of attribute name to field /// public Dictionary NameToParameter = new Dictionary(); /// /// Constructor /// /// Name of the task /// Task class to create /// Class type of an object to be constructed and passed as an argument to the task class constructor public ScriptTask(string InName, Type InTaskClass, Type InParametersClass) { Name = InName; TaskClass = InTaskClass; ParametersClass = InParametersClass; // Find all the fields which are tagged as parameters in ParametersClass foreach(FieldInfo Field in ParametersClass.GetFields()) { if(Field.MemberType == MemberTypes.Field) { TaskParameterAttribute ParameterAttribute = Field.GetCustomAttribute(); if(ParameterAttribute != null) { NameToParameter.Add(Field.Name, new ScriptTaskParameter(Field.Name, Field, ParameterAttribute.ValidationType, ParameterAttribute.Optional)); } } } } } /// /// Enumeration of standard types used in the schema. Avoids hard-coding names. /// enum ScriptSchemaStandardType { Graph, Trigger, TriggerBody, Agent, AgentBody, Node, NodeBody, Aggregate, Report, Badge, Notify, Include, Option, EnvVar, Property, Regex, Macro, Expand, Trace, Warning, Error, Name, NameList, Tag, TagList, NameOrTag, NameOrTagList, QualifiedName, BalancedString, Boolean, Integer, } /// /// Schema for build graph definitions. Stores information about the supported tasks, and allows validating an XML document. /// class ScriptSchema { /// /// Name of the root element /// public const string RootElementName = "BuildGraph"; /// /// Namespace for the schema /// public const string NamespaceURI = "http://www.epicgames.com/BuildGraph"; /// /// List of all the loaded classes which derive from BuildGraph.Task /// Dictionary NameToTask = new Dictionary(); /// /// Qualified name for the string type /// static readonly XmlQualifiedName StringTypeName = new XmlQualifiedName("string", "http://www.w3.org/2001/XMLSchema"); /// /// The inner xml schema /// public readonly XmlSchema CompiledSchema; /// /// Characters which are not permitted in names. /// public const string IllegalNameCharacters = "^<>:\"/\\|?*"; /// /// Pattern which matches any name; alphanumeric characters, with single embedded spaces. /// const string NamePattern = "[^ " + IllegalNameCharacters +"]+( [^ " + IllegalNameCharacters + "]+)*"; /// /// Pattern which matches a list of names, separated by semicolons. /// const string NameListPattern = NamePattern + "(;" + NamePattern + ")*"; /// /// Pattern which matches any tag name; a name with a leading '#' character /// const string TagPattern = "#" + NamePattern; /// /// Pattern which matches a list of tag names, separated by semicolons; /// const string TagListPattern = TagPattern + "(;" + TagPattern + ")*"; /// /// Pattern which matches any name or tag name; a name with a leading '#' character /// const string NameOrTagPattern = "#?" + NamePattern; /// /// Pattern which matches a list of names or tag names, separated by semicolons; /// const string NameOrTagListPattern = NameOrTagPattern + "(;" + NameOrTagPattern + ")*"; /// /// Pattern which matches a qualified name. /// const string QualifiedNamePattern = NamePattern + "(\\." + NamePattern + ")*"; /// /// Pattern which matches a property name /// const string PropertyPattern = "\\$\\(" + NamePattern + "\\)"; /// /// Pattern which matches balanced parentheses in a string /// const string StringWithPropertiesPattern = "[^\\$]*" + "(" + "(" + PropertyPattern + "|" + "\\$[^\\(]" + ")" + "[^\\$]*" + ")+" + "\\$?"; /// /// Pattern which matches balanced parentheses in a string /// const string BalancedStringPattern = "[^\\$]*" + "(" + "(" + PropertyPattern + "|" + "\\$[^\\(]" + ")" + "[^\\$]*" + ")*" + "\\$?"; /// /// Constructor /// /// Mapping of task name to information about how to construct it public ScriptSchema(Dictionary InNameToTask) { NameToTask = InNameToTask; // Create a lookup from standard types to their qualified names Dictionary TypeToSchemaTypeName = new Dictionary(); TypeToSchemaTypeName.Add(typeof(String), GetQualifiedTypeName(ScriptSchemaStandardType.BalancedString)); TypeToSchemaTypeName.Add(typeof(Boolean), GetQualifiedTypeName(ScriptSchemaStandardType.Boolean)); TypeToSchemaTypeName.Add(typeof(Int32), GetQualifiedTypeName(ScriptSchemaStandardType.Integer)); TypeToSchemaTypeName.Add(typeof(FileReference), GetQualifiedTypeName(ScriptSchemaStandardType.BalancedString)); TypeToSchemaTypeName.Add(typeof(DirectoryReference), GetQualifiedTypeName(ScriptSchemaStandardType.BalancedString)); TypeToSchemaTypeName.Add(typeof(UnrealTargetPlatform), GetQualifiedTypeName(ScriptSchemaStandardType.BalancedString)); // Create all the custom user types, and add them to the qualified name lookup List UserTypes = new List(); foreach(Type Type in NameToTask.Values.SelectMany(x => x.NameToParameter.Values).Select(x => x.ValueType)) { if(!TypeToSchemaTypeName.ContainsKey(Type)) { if(Type.IsClass && Type.GetInterfaces().Any(x => x.GetGenericTypeDefinition() == typeof(ICollection<>))) { TypeToSchemaTypeName.Add(Type, GetQualifiedTypeName(ScriptSchemaStandardType.BalancedString)); } else { string Name = Type.Name + "UserType"; XmlSchemaType SchemaType = CreateUserType(Name, Type); UserTypes.Add(SchemaType); TypeToSchemaTypeName.Add(Type, new XmlQualifiedName(Name, NamespaceURI)); } } } // Create all the task types Dictionary TaskNameToType = new Dictionary(); foreach(ScriptTask Task in NameToTask.Values) { XmlSchemaComplexType TaskType = new XmlSchemaComplexType(); TaskType.Name = Task.Name + "TaskType"; foreach(ScriptTaskParameter Parameter in Task.NameToParameter.Values) { XmlQualifiedName SchemaTypeName = GetQualifiedTypeName(Parameter.ValidationType); if(SchemaTypeName == null) { SchemaTypeName = TypeToSchemaTypeName[Parameter.ValueType]; } TaskType.Attributes.Add(CreateSchemaAttribute(Parameter.Name, SchemaTypeName, Parameter.bOptional? XmlSchemaUse.Optional : XmlSchemaUse.Required)); } TaskType.Attributes.Add(CreateSchemaAttribute("If", ScriptSchemaStandardType.BalancedString, XmlSchemaUse.Optional)); TaskNameToType.Add(Task.Name, TaskType); } // Create the schema object XmlSchema NewSchema = new XmlSchema(); NewSchema.TargetNamespace = NamespaceURI; NewSchema.ElementFormDefault = XmlSchemaForm.Qualified; NewSchema.Items.Add(CreateSchemaElement(RootElementName, ScriptSchemaStandardType.Graph)); NewSchema.Items.Add(CreateGraphType()); NewSchema.Items.Add(CreateTriggerType()); NewSchema.Items.Add(CreateTriggerBodyType()); NewSchema.Items.Add(CreateAgentType()); NewSchema.Items.Add(CreateAgentBodyType()); NewSchema.Items.Add(CreateNodeType()); NewSchema.Items.Add(CreateNodeBodyType(TaskNameToType)); NewSchema.Items.Add(CreateAggregateType()); NewSchema.Items.Add(CreateReportType()); NewSchema.Items.Add(CreateBadgeType()); NewSchema.Items.Add(CreateNotifyType()); NewSchema.Items.Add(CreateIncludeType()); NewSchema.Items.Add(CreateOptionType()); NewSchema.Items.Add(CreateEnvVarType()); NewSchema.Items.Add(CreatePropertyType()); NewSchema.Items.Add(CreateRegexType()); NewSchema.Items.Add(CreateMacroType()); NewSchema.Items.Add(CreateExpandType()); NewSchema.Items.Add(CreateDiagnosticType(ScriptSchemaStandardType.Trace)); NewSchema.Items.Add(CreateDiagnosticType(ScriptSchemaStandardType.Warning)); NewSchema.Items.Add(CreateDiagnosticType(ScriptSchemaStandardType.Error)); NewSchema.Items.Add(CreateSimpleTypeFromRegex(GetTypeName(ScriptSchemaStandardType.Name), "(" + NamePattern + "|" + StringWithPropertiesPattern + ")")); NewSchema.Items.Add(CreateSimpleTypeFromRegex(GetTypeName(ScriptSchemaStandardType.NameList), "(" + NameListPattern + "|" + StringWithPropertiesPattern + ")")); NewSchema.Items.Add(CreateSimpleTypeFromRegex(GetTypeName(ScriptSchemaStandardType.Tag), "(" + TagPattern + "|" + StringWithPropertiesPattern + ")")); NewSchema.Items.Add(CreateSimpleTypeFromRegex(GetTypeName(ScriptSchemaStandardType.TagList), "(" + TagListPattern + "|" + StringWithPropertiesPattern + ")")); NewSchema.Items.Add(CreateSimpleTypeFromRegex(GetTypeName(ScriptSchemaStandardType.NameOrTag), "(" + NameOrTagPattern + "|" + StringWithPropertiesPattern + ")")); NewSchema.Items.Add(CreateSimpleTypeFromRegex(GetTypeName(ScriptSchemaStandardType.NameOrTagList), "(" + NameOrTagListPattern + "|" + StringWithPropertiesPattern + ")")); NewSchema.Items.Add(CreateSimpleTypeFromRegex(GetTypeName(ScriptSchemaStandardType.QualifiedName), "(" + QualifiedNamePattern + "|" + StringWithPropertiesPattern + ")")); NewSchema.Items.Add(CreateSimpleTypeFromRegex(GetTypeName(ScriptSchemaStandardType.BalancedString), BalancedStringPattern)); NewSchema.Items.Add(CreateSimpleTypeFromRegex(GetTypeName(ScriptSchemaStandardType.Boolean), "(" + "true" + "|" + "false" + "|" + StringWithPropertiesPattern + ")")); NewSchema.Items.Add(CreateSimpleTypeFromRegex(GetTypeName(ScriptSchemaStandardType.Integer), "(" + "(-?[1-9][0-9]*|0)" + "|" + StringWithPropertiesPattern + ")")); foreach(XmlSchemaComplexType Type in TaskNameToType.Values) { NewSchema.Items.Add(Type); } foreach(XmlSchemaSimpleType Type in UserTypes) { NewSchema.Items.Add(Type); } // Now that we've finished, compile it and store it to the class XmlSchemaSet NewSchemaSet = new XmlSchemaSet(); NewSchemaSet.Add(NewSchema); NewSchemaSet.Compile(); foreach(XmlSchema NewCompiledSchema in NewSchemaSet.Schemas()) { CompiledSchema = NewCompiledSchema; } } /// /// Gets information about the task with the given name /// /// Name of the task /// Receives task info for the named task /// True if the task name was found and Task is set, false otherwise. public bool TryGetTask(string TaskName, out ScriptTask Task) { return NameToTask.TryGetValue(TaskName, out Task); } /// /// Export the schema to a file /// /// public void Export(FileReference File) { XmlWriterSettings Settings = new XmlWriterSettings(); Settings.Indent = true; Settings.IndentChars = " "; Settings.NewLineChars = "\n"; using(XmlWriter Writer = XmlWriter.Create(File.FullName, Settings)) { CompiledSchema.Write(Writer); } } /// /// Gets the bare name for the given script type /// /// Script type to find the name of /// Name of the schema type that matches the given script type static string GetTypeName(ScriptSchemaStandardType Type) { return Type.ToString() + "Type"; } /// /// Gets the qualified name for the given script type /// /// Script type to find the qualified name for /// Qualified name of the schema type that matches the given script type static XmlQualifiedName GetQualifiedTypeName(ScriptSchemaStandardType Type) { return new XmlQualifiedName(GetTypeName(Type), NamespaceURI); } /// /// Gets the qualified name of the schema type for the given type of validation /// /// Qualified name for the corresponding schema type static XmlQualifiedName GetQualifiedTypeName(TaskParameterValidationType Type) { switch(Type) { case TaskParameterValidationType.TagList: return GetQualifiedTypeName(ScriptSchemaStandardType.TagList); } return null; } /// /// Creates the schema type representing the graph type /// /// Type definition for a graph static XmlSchemaType CreateGraphType() { XmlSchemaChoice GraphChoice = new XmlSchemaChoice(); GraphChoice.MinOccurs = 0; GraphChoice.MaxOccursString = "unbounded"; GraphChoice.Items.Add(CreateSchemaElement("Include", ScriptSchemaStandardType.Include)); GraphChoice.Items.Add(CreateSchemaElement("Option", ScriptSchemaStandardType.Option)); GraphChoice.Items.Add(CreateSchemaElement("EnvVar", ScriptSchemaStandardType.EnvVar)); GraphChoice.Items.Add(CreateSchemaElement("Property", ScriptSchemaStandardType.Property)); GraphChoice.Items.Add(CreateSchemaElement("Regex", ScriptSchemaStandardType.Regex)); GraphChoice.Items.Add(CreateSchemaElement("Macro", ScriptSchemaStandardType.Macro)); GraphChoice.Items.Add(CreateSchemaElement("Agent", ScriptSchemaStandardType.Agent)); GraphChoice.Items.Add(CreateSchemaElement("Trigger", ScriptSchemaStandardType.Trigger)); GraphChoice.Items.Add(CreateSchemaElement("Aggregate", ScriptSchemaStandardType.Aggregate)); GraphChoice.Items.Add(CreateSchemaElement("Report", ScriptSchemaStandardType.Report)); GraphChoice.Items.Add(CreateSchemaElement("Badge", ScriptSchemaStandardType.Badge)); GraphChoice.Items.Add(CreateSchemaElement("Notify", ScriptSchemaStandardType.Notify)); GraphChoice.Items.Add(CreateSchemaElement("Trace", ScriptSchemaStandardType.Trace)); GraphChoice.Items.Add(CreateSchemaElement("Warning", ScriptSchemaStandardType.Warning)); GraphChoice.Items.Add(CreateSchemaElement("Error", ScriptSchemaStandardType.Error)); GraphChoice.Items.Add(CreateDoElement(ScriptSchemaStandardType.Graph)); GraphChoice.Items.Add(CreateSwitchElement(ScriptSchemaStandardType.Graph)); GraphChoice.Items.Add(CreateForEachElement(ScriptSchemaStandardType.Graph)); XmlSchemaComplexType GraphType = new XmlSchemaComplexType(); GraphType.Name = GetTypeName(ScriptSchemaStandardType.Graph); GraphType.Particle = GraphChoice; return GraphType; } /// /// Creates the schema type representing the trigger type /// /// Type definition for a trigger static XmlSchemaType CreateTriggerType() { XmlSchemaComplexContentExtension Extension = new XmlSchemaComplexContentExtension(); Extension.BaseTypeName = GetQualifiedTypeName(ScriptSchemaStandardType.TriggerBody); Extension.Attributes.Add(CreateSchemaAttribute("Name", ScriptSchemaStandardType.QualifiedName, XmlSchemaUse.Required)); Extension.Attributes.Add(CreateSchemaAttribute("If", ScriptSchemaStandardType.BalancedString, XmlSchemaUse.Optional)); XmlSchemaComplexContent ContentModel = new XmlSchemaComplexContent(); ContentModel.Content = Extension; XmlSchemaComplexType ComplexType = new XmlSchemaComplexType(); ComplexType.Name = GetTypeName(ScriptSchemaStandardType.Trigger); ComplexType.ContentModel = ContentModel; return ComplexType; } /// /// Creates the schema type representing the contents of a trigger type /// /// Type definition for an agent static XmlSchemaType CreateTriggerBodyType() { XmlSchemaChoice TriggerChoice = new XmlSchemaChoice(); TriggerChoice.MinOccurs = 0; TriggerChoice.MaxOccursString = "unbounded"; TriggerChoice.Items.Add(CreateSchemaElement("Property", ScriptSchemaStandardType.Property)); TriggerChoice.Items.Add(CreateSchemaElement("Regex", ScriptSchemaStandardType.Regex)); TriggerChoice.Items.Add(CreateSchemaElement("EnvVar", ScriptSchemaStandardType.EnvVar)); TriggerChoice.Items.Add(CreateSchemaElement("Agent", ScriptSchemaStandardType.Agent)); TriggerChoice.Items.Add(CreateSchemaElement("Aggregate", ScriptSchemaStandardType.Aggregate)); TriggerChoice.Items.Add(CreateSchemaElement("Trace", ScriptSchemaStandardType.Trace)); TriggerChoice.Items.Add(CreateSchemaElement("Warning", ScriptSchemaStandardType.Warning)); TriggerChoice.Items.Add(CreateSchemaElement("Error", ScriptSchemaStandardType.Error)); TriggerChoice.Items.Add(CreateDoElement(ScriptSchemaStandardType.TriggerBody)); TriggerChoice.Items.Add(CreateSwitchElement(ScriptSchemaStandardType.TriggerBody)); TriggerChoice.Items.Add(CreateForEachElement(ScriptSchemaStandardType.TriggerBody)); XmlSchemaComplexType TriggerType = new XmlSchemaComplexType(); TriggerType.Name = GetTypeName(ScriptSchemaStandardType.TriggerBody); TriggerType.Particle = TriggerChoice; return TriggerType; } /// /// Creates the schema type representing the agent type /// /// Type definition for an agent static XmlSchemaType CreateAgentType() { XmlSchemaComplexContentExtension Extension = new XmlSchemaComplexContentExtension(); Extension.BaseTypeName = GetQualifiedTypeName(ScriptSchemaStandardType.AgentBody); Extension.Attributes.Add(CreateSchemaAttribute("Name", StringTypeName, XmlSchemaUse.Required)); Extension.Attributes.Add(CreateSchemaAttribute("Type", ScriptSchemaStandardType.NameList, XmlSchemaUse.Optional)); Extension.Attributes.Add(CreateSchemaAttribute("If", ScriptSchemaStandardType.BalancedString, XmlSchemaUse.Optional)); XmlSchemaComplexContent ContentModel = new XmlSchemaComplexContent(); ContentModel.Content = Extension; XmlSchemaComplexType ComplexType = new XmlSchemaComplexType(); ComplexType.Name = GetTypeName(ScriptSchemaStandardType.Agent); ComplexType.ContentModel = ContentModel; return ComplexType; } /// /// Creates the schema type representing the contents of agent type /// /// Type definition for an agent static XmlSchemaType CreateAgentBodyType() { XmlSchemaChoice AgentChoice = new XmlSchemaChoice(); AgentChoice.MinOccurs = 0; AgentChoice.MaxOccursString = "unbounded"; AgentChoice.Items.Add(CreateSchemaElement("Property", ScriptSchemaStandardType.Property)); AgentChoice.Items.Add(CreateSchemaElement("Regex", ScriptSchemaStandardType.Regex)); AgentChoice.Items.Add(CreateSchemaElement("EnvVar", ScriptSchemaStandardType.EnvVar)); AgentChoice.Items.Add(CreateSchemaElement("Node", ScriptSchemaStandardType.Node)); AgentChoice.Items.Add(CreateSchemaElement("Trace", ScriptSchemaStandardType.Trace)); AgentChoice.Items.Add(CreateSchemaElement("Warning", ScriptSchemaStandardType.Warning)); AgentChoice.Items.Add(CreateSchemaElement("Error", ScriptSchemaStandardType.Error)); AgentChoice.Items.Add(CreateDoElement(ScriptSchemaStandardType.AgentBody)); AgentChoice.Items.Add(CreateSwitchElement(ScriptSchemaStandardType.AgentBody)); AgentChoice.Items.Add(CreateForEachElement(ScriptSchemaStandardType.AgentBody)); XmlSchemaComplexType AgentType = new XmlSchemaComplexType(); AgentType.Name = GetTypeName(ScriptSchemaStandardType.AgentBody); AgentType.Particle = AgentChoice; return AgentType; } /// /// Creates the schema type representing the node type /// /// Type definition for a node static XmlSchemaType CreateNodeType() { XmlSchemaComplexContentExtension Extension = new XmlSchemaComplexContentExtension(); Extension.BaseTypeName = GetQualifiedTypeName(ScriptSchemaStandardType.NodeBody); Extension.Attributes.Add(CreateSchemaAttribute("Name", ScriptSchemaStandardType.Name, XmlSchemaUse.Required)); Extension.Attributes.Add(CreateSchemaAttribute("Requires", ScriptSchemaStandardType.NameOrTagList, XmlSchemaUse.Optional)); Extension.Attributes.Add(CreateSchemaAttribute("Produces", ScriptSchemaStandardType.TagList, XmlSchemaUse.Optional)); Extension.Attributes.Add(CreateSchemaAttribute("After", ScriptSchemaStandardType.NameOrTagList, XmlSchemaUse.Optional)); Extension.Attributes.Add(CreateSchemaAttribute("Token", ScriptSchemaStandardType.BalancedString, XmlSchemaUse.Optional)); Extension.Attributes.Add(CreateSchemaAttribute("If", ScriptSchemaStandardType.BalancedString, XmlSchemaUse.Optional)); Extension.Attributes.Add(CreateSchemaAttribute("RunEarly", ScriptSchemaStandardType.Boolean, XmlSchemaUse.Optional)); Extension.Attributes.Add(CreateSchemaAttribute("NotifyOnWarnings", ScriptSchemaStandardType.Boolean, XmlSchemaUse.Optional)); XmlSchemaComplexContent ContentModel = new XmlSchemaComplexContent(); ContentModel.Content = Extension; XmlSchemaComplexType ComplexType = new XmlSchemaComplexType(); ComplexType.Name = GetTypeName(ScriptSchemaStandardType.Node); ComplexType.ContentModel = ContentModel; return ComplexType; } /// /// Creates the schema type representing the body of the node type /// /// Type definition for a node static XmlSchemaType CreateNodeBodyType(Dictionary TaskNameToType) { XmlSchemaChoice NodeChoice = new XmlSchemaChoice(); NodeChoice.MinOccurs = 0; NodeChoice.MaxOccursString = "unbounded"; NodeChoice.Items.Add(CreateSchemaElement("Property", ScriptSchemaStandardType.Property)); NodeChoice.Items.Add(CreateSchemaElement("Regex", ScriptSchemaStandardType.Regex)); NodeChoice.Items.Add(CreateSchemaElement("EnvVar", ScriptSchemaStandardType.EnvVar)); NodeChoice.Items.Add(CreateSchemaElement("Trace", ScriptSchemaStandardType.Trace)); NodeChoice.Items.Add(CreateSchemaElement("Warning", ScriptSchemaStandardType.Warning)); NodeChoice.Items.Add(CreateSchemaElement("Error", ScriptSchemaStandardType.Error)); NodeChoice.Items.Add(CreateSchemaElement("Expand", ScriptSchemaStandardType.Expand)); NodeChoice.Items.Add(CreateDoElement(ScriptSchemaStandardType.NodeBody)); NodeChoice.Items.Add(CreateSwitchElement(ScriptSchemaStandardType.NodeBody)); NodeChoice.Items.Add(CreateForEachElement(ScriptSchemaStandardType.NodeBody)); foreach (KeyValuePair Pair in TaskNameToType.OrderBy(x => x.Key)) { NodeChoice.Items.Add(CreateSchemaElement(Pair.Key, new XmlQualifiedName(Pair.Value.Name, NamespaceURI))); } XmlSchemaComplexType NodeType = new XmlSchemaComplexType(); NodeType.Name = GetTypeName(ScriptSchemaStandardType.NodeBody); NodeType.Particle = NodeChoice; return NodeType; } /// /// Creates the schema type representing the aggregate type /// /// Type definition for an aggregate static XmlSchemaType CreateAggregateType() { XmlSchemaComplexType AggregateType = new XmlSchemaComplexType(); AggregateType.Name = GetTypeName(ScriptSchemaStandardType.Aggregate); AggregateType.Attributes.Add(CreateSchemaAttribute("Name", ScriptSchemaStandardType.Name, XmlSchemaUse.Required)); AggregateType.Attributes.Add(CreateSchemaAttribute("Requires", ScriptSchemaStandardType.NameOrTagList, XmlSchemaUse.Required)); AggregateType.Attributes.Add(CreateSchemaAttribute("If", ScriptSchemaStandardType.BalancedString, XmlSchemaUse.Optional)); return AggregateType; } /// /// Creates the schema type representing the report type /// /// Type definition for a report static XmlSchemaType CreateReportType() { XmlSchemaComplexType ReportType = new XmlSchemaComplexType(); ReportType.Name = GetTypeName(ScriptSchemaStandardType.Report); ReportType.Attributes.Add(CreateSchemaAttribute("Name", ScriptSchemaStandardType.Name, XmlSchemaUse.Required)); ReportType.Attributes.Add(CreateSchemaAttribute("Requires", ScriptSchemaStandardType.NameOrTagList, XmlSchemaUse.Required)); ReportType.Attributes.Add(CreateSchemaAttribute("If", ScriptSchemaStandardType.BalancedString, XmlSchemaUse.Optional)); return ReportType; } /// /// Creates the schema type representing the badge type /// /// Type definition for a badge static XmlSchemaType CreateBadgeType() { XmlSchemaComplexType BadgeType = new XmlSchemaComplexType(); BadgeType.Name = GetTypeName(ScriptSchemaStandardType.Badge); BadgeType.Attributes.Add(CreateSchemaAttribute("Name", ScriptSchemaStandardType.Name, XmlSchemaUse.Required)); BadgeType.Attributes.Add(CreateSchemaAttribute("Requires", ScriptSchemaStandardType.NameOrTagList, XmlSchemaUse.Optional)); BadgeType.Attributes.Add(CreateSchemaAttribute("Targets", ScriptSchemaStandardType.NameOrTagList, XmlSchemaUse.Optional)); BadgeType.Attributes.Add(CreateSchemaAttribute("Project", ScriptSchemaStandardType.BalancedString, XmlSchemaUse.Required)); BadgeType.Attributes.Add(CreateSchemaAttribute("Change", ScriptSchemaStandardType.BalancedString, XmlSchemaUse.Optional)); BadgeType.Attributes.Add(CreateSchemaAttribute("If", ScriptSchemaStandardType.BalancedString, XmlSchemaUse.Optional)); return BadgeType; } /// /// Creates the schema type representing a notifier /// /// Type definition for a notifier static XmlSchemaType CreateNotifyType() { XmlSchemaComplexType AggregateType = new XmlSchemaComplexType(); AggregateType.Name = GetTypeName(ScriptSchemaStandardType.Notify); AggregateType.Attributes.Add(CreateSchemaAttribute("Targets", ScriptSchemaStandardType.NameOrTagList, XmlSchemaUse.Optional)); AggregateType.Attributes.Add(CreateSchemaAttribute("Except", ScriptSchemaStandardType.NameOrTagList, XmlSchemaUse.Optional)); AggregateType.Attributes.Add(CreateSchemaAttribute("Nodes", ScriptSchemaStandardType.NameOrTagList, XmlSchemaUse.Optional)); AggregateType.Attributes.Add(CreateSchemaAttribute("Triggers", ScriptSchemaStandardType.NameList, XmlSchemaUse.Optional)); AggregateType.Attributes.Add(CreateSchemaAttribute("Reports", ScriptSchemaStandardType.NameList, XmlSchemaUse.Optional)); AggregateType.Attributes.Add(CreateSchemaAttribute("Users", ScriptSchemaStandardType.BalancedString, XmlSchemaUse.Optional)); AggregateType.Attributes.Add(CreateSchemaAttribute("Submitters", ScriptSchemaStandardType.BalancedString, XmlSchemaUse.Optional)); AggregateType.Attributes.Add(CreateSchemaAttribute("Warnings", ScriptSchemaStandardType.Boolean, XmlSchemaUse.Optional)); AggregateType.Attributes.Add(CreateSchemaAttribute("If", ScriptSchemaStandardType.BalancedString, XmlSchemaUse.Optional)); AggregateType.Attributes.Add(CreateSchemaAttribute("Absolute", ScriptSchemaStandardType.BalancedString, XmlSchemaUse.Optional)); return AggregateType; } /// /// Creates the schema type representing an include type /// /// Type definition for an include directive static XmlSchemaType CreateIncludeType() { XmlSchemaComplexType PropertyType = new XmlSchemaComplexType(); PropertyType.Name = GetTypeName(ScriptSchemaStandardType.Include); PropertyType.Attributes.Add(CreateSchemaAttribute("Script", ScriptSchemaStandardType.BalancedString, XmlSchemaUse.Required)); PropertyType.Attributes.Add(CreateSchemaAttribute("If", ScriptSchemaStandardType.BalancedString, XmlSchemaUse.Optional)); return PropertyType; } /// /// Creates the schema type representing a parameter type /// /// Type definition for a parameter static XmlSchemaType CreateOptionType() { XmlSchemaComplexType OptionType = new XmlSchemaComplexType(); OptionType.Name = GetTypeName(ScriptSchemaStandardType.Option); OptionType.Attributes.Add(CreateSchemaAttribute("Name", ScriptSchemaStandardType.Name, XmlSchemaUse.Required)); OptionType.Attributes.Add(CreateSchemaAttribute("Restrict", StringTypeName, XmlSchemaUse.Optional)); OptionType.Attributes.Add(CreateSchemaAttribute("DefaultValue", StringTypeName, XmlSchemaUse.Required)); OptionType.Attributes.Add(CreateSchemaAttribute("Description", StringTypeName, XmlSchemaUse.Required)); OptionType.Attributes.Add(CreateSchemaAttribute("If", ScriptSchemaStandardType.BalancedString, XmlSchemaUse.Optional)); return OptionType; } /// /// Creates the schema type representing a environment variable type /// /// Type definition for an environment variable property static XmlSchemaType CreateEnvVarType() { XmlSchemaComplexType EnvVarType = new XmlSchemaComplexType(); EnvVarType.Name = GetTypeName(ScriptSchemaStandardType.EnvVar); EnvVarType.Attributes.Add(CreateSchemaAttribute("Name", ScriptSchemaStandardType.Name, XmlSchemaUse.Required)); EnvVarType.Attributes.Add(CreateSchemaAttribute("If", ScriptSchemaStandardType.BalancedString, XmlSchemaUse.Optional)); return EnvVarType; } /// /// Creates the schema type representing a property type /// /// Type definition for a property static XmlSchemaType CreatePropertyType() { XmlSchemaSimpleContentExtension Extension = new XmlSchemaSimpleContentExtension(); Extension.BaseTypeName = StringTypeName; Extension.Attributes.Add(CreateSchemaAttribute("Name", ScriptSchemaStandardType.Name, XmlSchemaUse.Required)); Extension.Attributes.Add(CreateSchemaAttribute("Value", StringTypeName, XmlSchemaUse.Optional)); Extension.Attributes.Add(CreateSchemaAttribute("If", ScriptSchemaStandardType.BalancedString, XmlSchemaUse.Optional)); XmlSchemaSimpleContent ContentModel = new XmlSchemaSimpleContent(); ContentModel.Content = Extension; XmlSchemaComplexType PropertyType = new XmlSchemaComplexType(); PropertyType.Name = GetTypeName(ScriptSchemaStandardType.Property); PropertyType.ContentModel = ContentModel; return PropertyType; } /// /// Creates the schema type representing a regex type /// /// Type definition for a regex static XmlSchemaType CreateRegexType() { XmlSchemaSimpleContentExtension Extension = new XmlSchemaSimpleContentExtension(); Extension.BaseTypeName = StringTypeName; Extension.Attributes.Add(CreateSchemaAttribute("Input", StringTypeName, XmlSchemaUse.Required)); Extension.Attributes.Add(CreateSchemaAttribute("Pattern", StringTypeName, XmlSchemaUse.Required)); Extension.Attributes.Add(CreateSchemaAttribute("Capture", ScriptSchemaStandardType.NameList, XmlSchemaUse.Required)); Extension.Attributes.Add(CreateSchemaAttribute("If", ScriptSchemaStandardType.BalancedString, XmlSchemaUse.Optional)); XmlSchemaSimpleContent ContentModel = new XmlSchemaSimpleContent(); ContentModel.Content = Extension; XmlSchemaComplexType RegexType = new XmlSchemaComplexType(); RegexType.Name = GetTypeName(ScriptSchemaStandardType.Regex); RegexType.ContentModel = ContentModel; return RegexType; } /// /// Creates the schema type representing the macro type /// /// Type definition for a node static XmlSchemaType CreateMacroType() { XmlSchemaComplexContentExtension Extension = new XmlSchemaComplexContentExtension(); Extension.BaseTypeName = GetQualifiedTypeName(ScriptSchemaStandardType.NodeBody); Extension.Attributes.Add(CreateSchemaAttribute("Name", ScriptSchemaStandardType.Name, XmlSchemaUse.Required)); Extension.Attributes.Add(CreateSchemaAttribute("Arguments", ScriptSchemaStandardType.BalancedString, XmlSchemaUse.Optional)); Extension.Attributes.Add(CreateSchemaAttribute("OptionalArguments", ScriptSchemaStandardType.BalancedString, XmlSchemaUse.Optional)); Extension.Attributes.Add(CreateSchemaAttribute("If", ScriptSchemaStandardType.BalancedString, XmlSchemaUse.Optional)); XmlSchemaComplexContent ContentModel = new XmlSchemaComplexContent(); ContentModel.Content = Extension; XmlSchemaComplexType ComplexType = new XmlSchemaComplexType(); ComplexType.Name = GetTypeName(ScriptSchemaStandardType.Macro); ComplexType.ContentModel = ContentModel; return ComplexType; } /// /// Creates the schema type representing a macro expansion /// /// Type definition for expanding a macro static XmlSchemaType CreateExpandType() { XmlSchemaAnyAttribute AnyAttribute = new XmlSchemaAnyAttribute(); AnyAttribute.ProcessContents = XmlSchemaContentProcessing.Skip; XmlSchemaComplexType PropertyType = new XmlSchemaComplexType(); PropertyType.Name = GetTypeName(ScriptSchemaStandardType.Expand); PropertyType.Attributes.Add(CreateSchemaAttribute("Name", ScriptSchemaStandardType.BalancedString, XmlSchemaUse.Required)); PropertyType.Attributes.Add(CreateSchemaAttribute("If", ScriptSchemaStandardType.BalancedString, XmlSchemaUse.Optional)); PropertyType.AnyAttribute = AnyAttribute; return PropertyType; } /// /// Creates the schema type representing a warning or error type /// /// Type definition for a warning static XmlSchemaType CreateDiagnosticType(ScriptSchemaStandardType StandardType) { XmlSchemaComplexType PropertyType = new XmlSchemaComplexType(); PropertyType.Name = GetTypeName(StandardType); PropertyType.Attributes.Add(CreateSchemaAttribute("Message", ScriptSchemaStandardType.BalancedString, XmlSchemaUse.Required)); PropertyType.Attributes.Add(CreateSchemaAttribute("If", ScriptSchemaStandardType.BalancedString, XmlSchemaUse.Optional)); return PropertyType; } /// /// Creates an element representing a conditional "Do" block, which recursively contains another type /// /// The base type for the do block to contain /// New schema element for the block static XmlSchemaElement CreateDoElement(ScriptSchemaStandardType InnerType) { XmlSchemaComplexContentExtension Extension = new XmlSchemaComplexContentExtension(); Extension.BaseTypeName = GetQualifiedTypeName(InnerType); Extension.Attributes.Add(CreateSchemaAttribute("If", ScriptSchemaStandardType.BalancedString, XmlSchemaUse.Optional)); XmlSchemaComplexContent ContentModel = new XmlSchemaComplexContent(); ContentModel.Content = Extension; XmlSchemaComplexType SchemaType = new XmlSchemaComplexType(); SchemaType.ContentModel = ContentModel; XmlSchemaElement Element = new XmlSchemaElement(); Element.Name = "Do"; Element.SchemaType = SchemaType; return Element; } /// /// Creates an element representing a conditional "Switch" block, which recursively contains another type /// /// The base type for the do block to contain /// New schema element for the block static XmlSchemaElement CreateSwitchElement(ScriptSchemaStandardType InnerType) { // Create the "Option" element XmlSchemaComplexContentExtension CaseExtension = new XmlSchemaComplexContentExtension(); CaseExtension.BaseTypeName = GetQualifiedTypeName(InnerType); CaseExtension.Attributes.Add(CreateSchemaAttribute("If", ScriptSchemaStandardType.BalancedString, XmlSchemaUse.Required)); XmlSchemaComplexContent CaseContentModel = new XmlSchemaComplexContent(); CaseContentModel.Content = CaseExtension; XmlSchemaComplexType CaseSchemaType = new XmlSchemaComplexType(); CaseSchemaType.ContentModel = CaseContentModel; XmlSchemaElement CaseElement = new XmlSchemaElement(); CaseElement.Name = "Case"; CaseElement.SchemaType = CaseSchemaType; CaseElement.MinOccurs = 0; CaseElement.MaxOccursString = "unbounded"; // Create the "Otherwise" element XmlSchemaElement OtherwiseElement = new XmlSchemaElement(); OtherwiseElement.Name = "Default"; OtherwiseElement.SchemaTypeName = GetQualifiedTypeName(InnerType); OtherwiseElement.MinOccurs = 0; OtherwiseElement.MaxOccurs = 1; // Create the "Switch" element XmlSchemaSequence SwitchSequence = new XmlSchemaSequence(); SwitchSequence.Items.Add(CaseElement); SwitchSequence.Items.Add(OtherwiseElement); XmlSchemaComplexType SwitchSchemaType = new XmlSchemaComplexType(); SwitchSchemaType.Particle = SwitchSequence; XmlSchemaElement SwitchElement = new XmlSchemaElement(); SwitchElement.Name = "Switch"; SwitchElement.SchemaType = SwitchSchemaType; return SwitchElement; } /// /// Creates an element representing a conditional "ForEach" block, which recursively contains another type /// /// The base type for the foreach block to contain /// New schema element for the block static XmlSchemaElement CreateForEachElement(ScriptSchemaStandardType InnerType) { XmlSchemaComplexContentExtension Extension = new XmlSchemaComplexContentExtension(); Extension.BaseTypeName = GetQualifiedTypeName(InnerType); Extension.Attributes.Add(CreateSchemaAttribute("Name", ScriptSchemaStandardType.BalancedString, XmlSchemaUse.Required)); Extension.Attributes.Add(CreateSchemaAttribute("Values", ScriptSchemaStandardType.BalancedString, XmlSchemaUse.Required)); Extension.Attributes.Add(CreateSchemaAttribute("Separator", ScriptSchemaStandardType.BalancedString, XmlSchemaUse.Optional)); Extension.Attributes.Add(CreateSchemaAttribute("If", ScriptSchemaStandardType.BalancedString, XmlSchemaUse.Optional)); XmlSchemaComplexContent ContentModel = new XmlSchemaComplexContent(); ContentModel.Content = Extension; XmlSchemaComplexType SchemaType = new XmlSchemaComplexType(); SchemaType.ContentModel = ContentModel; XmlSchemaElement Element = new XmlSchemaElement(); Element.Name = "ForEach"; Element.SchemaType = SchemaType; return Element; } /// /// Constructs an XmlSchemaElement and initializes it with the given parameters /// /// Element name /// Type enumeration for the attribute /// A new XmlSchemaElement object static XmlSchemaElement CreateSchemaElement(string Name, ScriptSchemaStandardType SchemaType) { return CreateSchemaElement(Name, GetQualifiedTypeName(SchemaType)); } /// /// Constructs an XmlSchemaElement and initializes it with the given parameters /// /// Element name /// Qualified name of the type for this element /// A new XmlSchemaElement object static XmlSchemaElement CreateSchemaElement(string Name, XmlQualifiedName SchemaTypeName) { XmlSchemaElement Element = new XmlSchemaElement(); Element.Name = Name; Element.SchemaTypeName = SchemaTypeName; return Element; } /// /// Constructs an XmlSchemaAttribute and initialize it with the given parameters /// /// The attribute name /// Type enumeration for the attribute /// Whether the attribute is required or optional /// A new XmlSchemaAttribute object static XmlSchemaAttribute CreateSchemaAttribute(string Name, ScriptSchemaStandardType SchemaType, XmlSchemaUse Use) { return CreateSchemaAttribute(Name, GetQualifiedTypeName(SchemaType), Use); } /// /// Constructs an XmlSchemaAttribute and initialize it with the given parameters /// /// The attribute name /// Qualified name of the type for this attribute /// Whether the attribute is required or optional /// The new attribute static XmlSchemaAttribute CreateSchemaAttribute(string Name, XmlQualifiedName SchemaTypeName, XmlSchemaUse Use) { XmlSchemaAttribute Attribute = new XmlSchemaAttribute(); Attribute.Name = Name; Attribute.SchemaTypeName = SchemaTypeName; Attribute.Use = Use; return Attribute; } /// /// Creates a simple type that is the union of two other types /// /// The name of the type /// List of valid types for the union /// A simple type which will match the given pattern static XmlSchemaSimpleType CreateSimpleTypeFromUnion(string Name, params XmlSchemaType[] ValidTypes) { XmlSchemaSimpleTypeUnion Union = new XmlSchemaSimpleTypeUnion(); foreach (XmlSchemaType ValidType in ValidTypes) { Union.BaseTypes.Add(ValidType); } XmlSchemaSimpleType UnionType = new XmlSchemaSimpleType(); UnionType.Name = Name; UnionType.Content = Union; return UnionType; } /// /// Creates a simple type that matches a regex /// /// Name of the new type /// Regex pattern to match /// A simple type which will match the given pattern static XmlSchemaSimpleType CreateSimpleTypeFromRegex(string Name, string Pattern) { XmlSchemaPatternFacet PatternFacet = new XmlSchemaPatternFacet(); PatternFacet.Value = Pattern; XmlSchemaSimpleTypeRestriction Restriction = new XmlSchemaSimpleTypeRestriction(); Restriction.BaseTypeName = StringTypeName; Restriction.Facets.Add(PatternFacet); XmlSchemaSimpleType SimpleType = new XmlSchemaSimpleType(); SimpleType.Name = Name; SimpleType.Content = Restriction; return SimpleType; } /// /// Create a schema type for the given user type. Currently only handles enumerations. /// /// Name for the new type /// CLR type information to create a schema type for static XmlSchemaType CreateUserType(string Name, Type Type) { if(Type.IsEnum) { return CreateSimpleTypeFromUnion(Name, CreateEnumType(null, Type), CreateSimpleTypeFromRegex(null, StringWithPropertiesPattern)); } else { throw new AutomationException("Cannot create custom type in schema for '{0}'", Type.Name); } } /// /// Create a schema type for the given enum. /// /// Name for the new type /// CLR type information to create a schema type for static XmlSchemaType CreateEnumType(string Name, Type Type) { XmlSchemaSimpleTypeRestriction Restriction = new XmlSchemaSimpleTypeRestriction(); Restriction.BaseTypeName = StringTypeName; foreach(string EnumName in Enum.GetNames(Type)) { XmlSchemaEnumerationFacet Facet = new XmlSchemaEnumerationFacet(); Facet.Value = EnumName; Restriction.Facets.Add(Facet); } XmlSchemaSimpleType SchemaType = new XmlSchemaSimpleType(); SchemaType.Name = Name; SchemaType.Content = Restriction; return SchemaType; } } }