using Json.LitJson; using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; namespace ServiceClientGenerator { #region Simplifications #region SimpleConstructorsModel /// /// A model that represents the simple constructors of a structure. These constructors are specified in customizations /// Simple constructors are constructors that are added to request objects that take parameters to set members of the object. /// This allows for easier creation of these request objects. /// public class SimpleConstructorsModel { public const string ConstructorsKey = "constructors"; JsonData _documentRoot; readonly bool _emptyData = false; /// /// Creates a model for simple constructors for the structure. Used to customize the generation of the structure /// /// The json data that contains information about the customization public SimpleConstructorsModel(JsonData root) { if (root.PropertyNames.Contains("simpleConstructors", StringComparer.OrdinalIgnoreCase)) { _documentRoot = root["simpleConstructors"]; } else { _documentRoot = new JsonData(); _emptyData = true; } } /// /// Sets the document root from a json text /// /// The reader to get the json text from void Initialize(TextReader reader) { this._documentRoot = JsonMapper.ToObject(reader); } /// /// A dictionary that maps structure names to the form of the constructor /// public Dictionary SimpleConstructors { get { var constructors = new Dictionary(StringComparer.OrdinalIgnoreCase); var data = _documentRoot[ConstructorsKey]; if (data == null) return constructors; foreach (KeyValuePair kvp in data) { constructors[kvp.Key] = new ConstructorForms(kvp.Key, kvp.Value); } return constructors; } } /// /// Determines if custom constructors need to be generated for the structure /// /// The name of the structure to check for /// True if there are custom simple constructors public bool CreateSimpleConstructors(string className) { if (_emptyData) return false; return SimpleConstructors.ContainsKey(className); } /// /// A list of members for the structure that are used in the constructor form /// /// A list of the member names used by the constructor form /// All of the members for the structure /// The members for the form public List GetFormMembers(List form, IList operationMembers) { var docMembers = new List(); foreach (var mem in form) { foreach (var opmem in operationMembers) { if (opmem.PropertyName.Equals(mem, StringComparison.OrdinalIgnoreCase)) { docMembers.Add(opmem); break; } } } return docMembers; } /// /// Used to generate a string used to define the params for the constructor /// /// List of member names for the constructor form /// All of the members of the structure /// A string that represents the required params for the constructor form public string GetSimpleParameters(List form, IList operationMembers) { string currentParams = ""; foreach (var mem in form) { Member member = null; foreach (var opmem in operationMembers) { if (opmem.PropertyName.Equals(mem, StringComparison.OrdinalIgnoreCase)) { member = opmem; break; } } if (member == null) throw new ArgumentOutOfRangeException("Unable to find matching member"); if (currentParams == "") currentParams = member.DetermineType() + " " + GeneratorHelpers.CamelCaseParam(member.PropertyName); else currentParams = currentParams + ", " + member.DetermineType() + " " + GeneratorHelpers.CamelCaseParam(member.PropertyName); } return currentParams; } } #endregion #region ConstructorForms /// /// Represents forms of the constructor /// The constructor forms are the different parameter options of a constructor /// public class ConstructorForms { public const string ConstructorFormsKey = "constructorForms"; readonly JsonData _constructorRoot; readonly string _name; public string Name { get { return _name; } } /// /// Creates a representation for a constructor in the structure consisting of a list of a list of member names /// /// The name of the constructor /// The json customization for the constructor forms public ConstructorForms(string name, JsonData json) { this._name = name; this._constructorRoot = json; } /// /// A list of forms for the constructor consisting of the names of members in each form /// public List> Forms { get { var forms = new List>(); var data = _constructorRoot[ConstructorFormsKey]; if (data == null || !data.IsArray) return forms; foreach (var formData in data) { var form = new List(); foreach (var item in (formData as JsonData)) { form.Add(item.ToString()); } forms.Add(form); } return forms; } } } #endregion #region SimpleMethodFormsModel /// /// Represents custom simple methods for the client being generated from the service model /// Simple methods are methods added to the client that take members of the request as parameters and then creates a request with those /// members internally and then calls the operation /// public class SimpleMethodFormsModel { public const string SimpleMethodsKey = "simpleMethods"; readonly JsonData _documentRoot; readonly bool _emptyData = false; /// /// Creates a representation of the simple method customizations for the service client /// /// The json data that specifies forms of the simple methods public SimpleMethodFormsModel(JsonData root) { if (root.PropertyNames.Contains(SimpleMethodsKey, StringComparer.OrdinalIgnoreCase)) { _documentRoot = root[SimpleMethodsKey]; } else { _documentRoot = new JsonData(); _emptyData = true; } } /// /// A mapping of method names to the forms for that simple method /// public Dictionary SimpleMethods { get { var methods = new Dictionary(StringComparer.OrdinalIgnoreCase); var data = _documentRoot[ServiceModel.OperationsKey]; if (data == null) return methods; foreach (KeyValuePair kvp in data) { methods[kvp.Key] = new MethodForms(kvp.Key, kvp.Value); } return methods; } } /// /// Determines if the operation in the client has a custom simple method specified /// /// The name of the operation /// If the operation is specified in the customizations public bool CreateSimpleMethods(string operationName) { if (_emptyData) return false; return SimpleMethods.ContainsKey(operationName); } /// /// Finds the members from the request that need to be used in the method /// /// List of member names for the given form of an operation /// All of the members for the given operation /// public List GetFormMembers(List form, IList operationMembers) { var docMembers = new List(); foreach (var mem in form) { foreach (var opmem in operationMembers) { if (opmem.PropertyName.Equals(mem, StringComparison.OrdinalIgnoreCase)) { docMembers.Add(opmem); break; } } } return docMembers; } /// /// Generates the code for parameters of the method /// /// The form of the method contains names of all the members used /// All of the members of an operation /// A proper syntax for params of the simple method form public string GetSimpleParameters(List form, IList operationMembers) { string currentParams = ""; foreach (var mem in form) { Member member = null; foreach (var opmem in operationMembers) { if (opmem.PropertyName.Equals(mem, StringComparison.OrdinalIgnoreCase)) { member = opmem; break; } } if (member == null) throw new ArgumentOutOfRangeException("Unable to find matching member"); string type = member.DetermineType(); if (type == "Stream") type = "System.IO.Stream"; if (currentParams == "") currentParams = type + " " + GeneratorHelpers.CamelCaseParam(member.PropertyName); else currentParams = currentParams + ", " + type + " " + GeneratorHelpers.CamelCaseParam(member.PropertyName); } return currentParams; } } #endregion #region MethodForms /// /// Represents the forms of a custom simple method /// Forms of a simple method are the variety of parameters the method will have /// public class MethodForms { public const string MethodFormsKey = "methodForms"; readonly JsonData _methodRoot; readonly string _name; public string Name { get { return _name; } } /// /// Creates a representation of the forms for a custom simple method /// /// The name of the method /// The json model for the customization consisting of a list of forms public MethodForms(string name, JsonData json) { this._name = name; this._methodRoot = json; } /// /// The forms of a method which is a list of a list of member names for that form /// public List> Forms { get { var forms = new List>(); var data = _methodRoot[MethodFormsKey]; if (data == null || !data.IsArray) return forms; foreach (var formData in data) { var form = new List(); foreach (var item in (formData as JsonData)) { form.Add(item.ToString()); } forms.Add(form); } return forms; } } } #endregion #endregion #region CustomizationsModel /// /// A model used to represent different types of customizations for generating a service /// public class CustomizationsModel { public const string RetainOriginalMemberOrderingKey = "retainOriginalMemberOrdering"; public const string RuntimePipelineOverrideKey = "runtimePipelineOverride"; public const string OverridesKey = "overrides"; public const string OperationKey = "operation"; public const string TargetTypeKey = "targetType"; public const string NewTypeKey = "newType"; public const string ConstructorInputKey = "constructorInput"; public const string ConditionKey = "condition"; public const string NoArgOverloadsKey = "noArgOverloads"; public const string SuppressResultGenerationKey = "suppressResultGeneration"; public const string UseNullableTypeKey = "useNullableType"; public const string EmitIsSetPropertiesKey = "emitIsSetProperties"; public const string GlobalShapeKey = "*"; public const string ShapeSubstitutionsKey = "shapeSubstitutions"; public const string EmitAsShapeKey = "emitAsShape"; public const string RenameShapeKey = "renameShape"; public const string EmitFromMemberKey = "emitFromMember"; public const string ListMemberSuffixExclusionsKey = "listMemberSuffixExclusions"; public const string DataTypeSwapKey = "dataTypeSwap"; public const string TypeKey = "Type"; public const string MarshallerKey = "Marshaller"; public const string UnmarshallerKey = "Unmarshaller"; public const string SuppressSimpleMethodExceptionDocsKey = "suppressSimpleMethodExceptionDocs"; public const string XHttpMethodOverrideKey = "xHttpMethodOverride"; public const string DeprecationMessageKey = "message"; public const string ExamplesKey = "examples"; public const string GenerateUnmarshallerKey = "generateUnmarshaller"; public const string SkipUriPropertyValidationKey = "skipUriPropertyValidation"; public const string OverrideContentTypeKey = "overrideContentType"; JsonData _documentRoot; SimpleMethodFormsModel _simpleMethodsModel; SimpleConstructorsModel _simpleConstructorsModel; /// /// A model that represents any custom methods to be generated by the client generator /// public SimpleMethodFormsModel SimpleMethodsModel { get { return _simpleMethodsModel ?? (_simpleMethodsModel = new SimpleMethodFormsModel(_documentRoot)); } } /// /// A model that represents any simple constructors to be included while generating structures for the service /// public SimpleConstructorsModel SimpleConstructorsModel { get { return _simpleConstructorsModel ?? (_simpleConstructorsModel = new SimpleConstructorsModel(_documentRoot)); } } /// /// Creates a customization model used to get customizations for a service /// /// Reader to get the json text from public CustomizationsModel(TextReader reader) { if (reader == null) this._documentRoot = new JsonData(); else Initialize(reader); } /// /// Creates a customization model used to get customizations for a service /// /// Path to the file to read the customizations from public CustomizationsModel(string modelPath) { if (string.IsNullOrEmpty(modelPath)) _documentRoot = new JsonData(); else using (var reader = new StreamReader(modelPath)) Initialize(reader); } /// /// Sets the document root by parsing the json text /// /// The reader to get json text from void Initialize(TextReader reader) { this._documentRoot = JsonMapper.ToObject(reader); } RuntimePipelineOverride _runtimePipelineOverride; /// /// Allows customizing pipeline overrides to be added to the client /// public RuntimePipelineOverride PipelineOverride { get { if (_runtimePipelineOverride != null) { return _runtimePipelineOverride; } var data = _documentRoot[RuntimePipelineOverrideKey]; if (data != null) { _runtimePipelineOverride = new RuntimePipelineOverride(); foreach (var item in data[OverridesKey]) { var jsonData = (JsonData) item; _runtimePipelineOverride.Overrides.Add( new RuntimePipelineOverride.Override() { OverrideMethod = jsonData[OperationKey] != null ? jsonData[OperationKey].ToString() : null, TargetType = jsonData[TargetTypeKey] != null ? jsonData[TargetTypeKey].ToString() : null, NewType = jsonData[NewTypeKey] != null ? jsonData[NewTypeKey].ToString() : null, ConstructorInput = jsonData[ConstructorInputKey] != null ? jsonData[ConstructorInputKey].ToString() : "", Condition = jsonData[ConditionKey] != null ? jsonData[ConditionKey].ToString() : "" }); } } return _runtimePipelineOverride; } } public bool SuppressSimpleMethodExceptionDocs { get { var flag = _documentRoot[SuppressSimpleMethodExceptionDocsKey]; if (flag != null && flag.IsBoolean) { return (bool)flag; } else if (flag != null && flag.IsString) { return bool.Parse((string)flag); } return false; } } public bool RetainOriginalMemberOrdering { get { var flag = _documentRoot[RetainOriginalMemberOrderingKey]; if (flag != null && flag.IsBoolean) { return (bool)flag; } else if (flag != null && flag.IsString) { return bool.Parse((string)flag); } return false; } } /// /// A list of uri properties for the service where we should not do validation for presence. /// public List SkipUriPropertyValidations { get { var validations = new List(); var data = _documentRoot[SkipUriPropertyValidationKey]; if (data == null || !data.IsArray) return validations; foreach (var item in data) validations.Add(item.ToString()); return validations; } } /// /// A list of methods that will be customized to have no arguments as one form of the operation /// public List NoArgOverloads { get { var overloads = new List(); var data = _documentRoot[NoArgOverloadsKey]; if (data == null || !data.IsArray) return overloads; foreach (var item in data) overloads.Add(item.ToString()); overloads.Sort(); return overloads; } } private HashSet _resultGenerationSuppressions = null; /// /// A list of methods that will be customized to not have a derived /// Result class due to an empty response. /// public HashSet ResultGenerationSuppressions { get { if (_resultGenerationSuppressions == null) { _resultGenerationSuppressions = new HashSet(); var data = _documentRoot[SuppressResultGenerationKey]; if (data != null && data.IsArray) { foreach (var item in data) _resultGenerationSuppressions.Add(item.ToString()); } } return _resultGenerationSuppressions; } } public bool GenerateCustomUnmarshaller { get { var data = _documentRoot[GenerateUnmarshallerKey]; if (data == null) return false; return true; } } /// /// Override for the Content-Type header /// public string OverrideContentType { get { var data = _documentRoot[OverrideContentTypeKey]; if (data == null) { return null; } return (string)data; } } public List CustomUnmarshaller { get { var data = _documentRoot[GenerateUnmarshallerKey]; if (data == null) return null; return (from JsonData s in data select s.ToString()).ToList(); } } /// /// Determines if the operation has a customization for creating a no argument method /// /// The name of the operation to check for /// True if the operation is in the list of noarg customizations public bool CreateNoArgOverload(string operationName) { return NoArgOverloads.Contains(operationName, StringComparer.OrdinalIgnoreCase); } /// /// Gets the property modifier for a property in a shape (can be global) if so customized. /// /// The name of the shape containing the property /// The property name to look for /// The property modifier, null if not found public PropertyModifier GetPropertyModifier(string shapeName, string propertyName) { // for renames, check for a shape-specific rename first, then look to // see if the property is globally renamed if (ShapeModifiers.ContainsKey(shapeName)) { var shapeModifiers = ShapeModifiers[shapeName]; if (shapeModifiers.IsModified(propertyName)) return shapeModifiers.PropertyModifier(propertyName); } if (ShapeModifiers.ContainsKey(GlobalShapeKey)) { var globalShapeModifiers = ShapeModifiers[GlobalShapeKey]; if (globalShapeModifiers.IsModified(propertyName)) return globalShapeModifiers.PropertyModifier(propertyName); } return null; } /// /// Determines if the property has a customization to be set to nullable /// /// The name of the shape the property is in /// The name of the property /// If the property is to be nullable public bool UseNullable(string shapeName, string propertyName) { var data = _documentRoot[UseNullableTypeKey]; if (data == null) return false; var shape = data[shapeName] as JsonData; if (shape == null || !shape.IsArray) return false; foreach (var name in shape) { if (string.Equals(name.ToString(), propertyName)) return true; } return false; } /// /// Determines if the collection property can be empty when being /// sent to the service. /// /// The name of the shape the property is in /// The name of the property /// If the collection property can be empty public bool EmitIsSetProperties(string shapeName, string propertyName) { var data = _documentRoot[EmitIsSetPropertiesKey]; if (data == null) return false; var shape = data[shapeName] as JsonData; if (shape == null || !shape.IsArray) return false; foreach (var name in shape) { if (string.Equals(name.ToString(), propertyName)) return true; } return false; } public string GetOverrideShapeName(string originalShapeName) { return GetRemappedShapeName(originalShapeName, RenameShapeKey); } /// /// Returns the name of the shape that should be used instead of the supplied shape /// /// The shape to have its name changed /// The new name, or null if not found public string GetSubstituteShapeName(string originalShapeName) { return GetRemappedShapeName(originalShapeName, EmitAsShapeKey); } private string GetRemappedShapeName(string originalShapeName, string key) { var substitutionsData = _documentRoot[ShapeSubstitutionsKey]; if (substitutionsData == null) return null; var shapeRemapData = substitutionsData[originalShapeName] as JsonData; if (null == shapeRemapData || null == shapeRemapData[key]) return null; return (string)shapeRemapData[key]; } /// /// Returns the substitution entry for a shape /// /// /// public JsonData GetSubstituteShapeData(string originalShapeName) { var substitutionsData = _documentRoot[ShapeSubstitutionsKey]; if (substitutionsData == null) return null; return substitutionsData[originalShapeName]; } /// /// Determines if the shape should be substituted with another /// /// The original shape /// If the shape is in the rename shapes public bool IsSubstitutedShape(string originalShapeName) { var substitutionsData = _documentRoot[ShapeSubstitutionsKey]; if (substitutionsData == null) return false; var remapShape = substitutionsData[originalShapeName] as JsonData; return remapShape != null; } /// /// Gets any customizations for a new data type to be used on a property /// /// The name of the shape the property is defined within /// The name of the property to check /// The override datatype object, null if not found public DataTypeOverride OverrideDataType(string shapeName, string propertyName) { var data = _documentRoot[DataTypeSwapKey]; if (data == null) return null; var shape = data[shapeName] as JsonData; if (shape == null) return null; var jsonData = shape[propertyName]; if (jsonData == null) return null; var dataType = (string)jsonData[TypeKey]; string marshaller = null; if (jsonData[MarshallerKey] != null && jsonData[MarshallerKey].IsString) marshaller = (string)jsonData[MarshallerKey]; string unmarshaller = null; if (jsonData[UnmarshallerKey] != null && jsonData[UnmarshallerKey].IsString) unmarshaller = (string)jsonData[UnmarshallerKey]; return new DataTypeOverride(dataType, marshaller, unmarshaller); } #region ShapeModifier /// /// The ShapeModifier allows shapes pulled from the model to be customized by excluding /// properties, modifying them (rename etc) and injecting properties not in the original /// model. /// public class ShapeModifier { public const string ShapeModifiersKey = "shapeModifiers"; public const string ExcludeKey = "exclude"; public const string ModifyKey = "modify"; public const string InjectKey = "inject"; public const string CustomMarshallKey = "customMarshall"; public const string DeprecatedMessageKey = "deprecatedMessage"; public const string BackwardsCompatibleDateTimeKey = "backwardsCompatibleDateTimeProperties"; private readonly HashSet _excludedProperties; private readonly HashSet _backwardsCompatibleDateTimeProperties; private readonly Dictionary _modifiedProperties; private readonly Dictionary _injectedProperties; public string DeprecationMessage { get; private set; } public ShapeModifier(JsonData data) { DeprecationMessage = data[DeprecatedMessageKey].CastToString(); _excludedProperties = ParseExclusions(data); _backwardsCompatibleDateTimeProperties = ParseBackwardsCompatibleDateTimeProperties(data); _modifiedProperties = ParseModifiers(data); // Process additions after rename to allow for models where we // add a 'convenience' member (for backwards compatibility) using // the same name as an original (and now renamed) member. _injectedProperties = ParseInjections(data); } #region Property Exclusion // Exclusion modifier is a simple array of property names. // "exclude": [ "propName1", "propName2" ] private static HashSet ParseExclusions(JsonData data) { var exclusions = data[ShapeModifier.ExcludeKey] ?.Cast() .Select(exclusion => exclusion.ToString()); return new HashSet(exclusions ?? new string[0]); } public bool IsExcludedProperty(string propertyName) { return _excludedProperties.Contains(propertyName); } #endregion #region Backwards Compatible DateTime Properties // Backwards Compatible DateTime Properties modifier is a simple array of property names. // "backwardsCompatibleDateTimeProperties": [ "propName1", "propName2" ] private static HashSet ParseBackwardsCompatibleDateTimeProperties(JsonData data) { var exclusions = data[ShapeModifier.BackwardsCompatibleDateTimeKey] ?.Cast() .Select(exclusion => exclusion.ToString()); return new HashSet(exclusions ?? new string[0]); } public bool IsBackwardsCompatibleDateTimeProperty(string propertyName) { return _backwardsCompatibleDateTimeProperties.Contains(propertyName); } #endregion #region Property Modifiers // A modifier is an array of objects, each object being the original // property name (key) and an object containing replacement name (value) // along with any custom marshal naming to apply. // "modify": [ // { // "modelPropertyName": { // "emitPropertyName": "userVisibleName", // "locationName": "customRequestMarshalName", // "emitFromMember": "subMember" // } // } // ] private static Dictionary ParseModifiers(JsonData data) { var modifiers = data[ShapeModifier.ModifyKey] ?.Cast() .Select(modifier => modifier.Cast>().First()); return modifiers?.ToDictionary(modifier => modifier.Key, modifier => modifier.Value) ?? new Dictionary(); } public bool IsModified(string propertyName) { return _modifiedProperties.ContainsKey(propertyName); } public PropertyModifier PropertyModifier(string propertyName) { if (IsModified(propertyName)) return new PropertyModifier(propertyName, _modifiedProperties[propertyName]); return null; } #endregion #region Property Injection // Injection modifier is an array of objects, each object being the // name of the member to add plus any needed shape/marshalling data. // "inject": [ // { "propertyName": // { "type": "list", // "member": { "shape": "String", "locationName": "item" } // } // } // ] // Since we don't have access to the model at this point, we simply store // the json data for the shape type to be used and 'hydrate' it when needed // externally private static Dictionary ParseInjections(JsonData data) { var injections = data[ShapeModifier.InjectKey] ?.Cast() .Select(modifier => modifier.Cast>().First()); return injections?.ToDictionary(modifier => modifier.Key, modifier => modifier.Value) ?? new Dictionary(); } public bool HasInjectedProperties { get { return _injectedProperties.Any(); } } public IEnumerable InjectedPropertyNames { get { var names = new List(); if (HasInjectedProperties) { names.AddRange(_injectedProperties.Keys); } return names; } } public PropertyInjector InjectedPropertyData(string propertyName) { if (!_injectedProperties.ContainsKey(propertyName)) throw new ArgumentException(); return new PropertyInjector(_injectedProperties[propertyName]); } #endregion } /// /// Injects a new property into a model shape /// public class PropertyInjector { private readonly JsonData _injectionData; internal PropertyInjector(JsonData injectionData) { this._injectionData = injectionData; } public JsonData Data { get { return _injectionData; } } public bool IsSetUseCustomMarshall { get { return _injectionData[ShapeModifier.CustomMarshallKey] != null; } } // If set, the generated request marshaller will skip the member in // favor of custom marshall logic in the pipeline public bool UseCustomMarshall { get { if (IsSetUseCustomMarshall) return (bool)_injectionData[ShapeModifier.CustomMarshallKey]; return false; } } } /// /// Property modifiers allow properties to be renamed within a shape /// as well as apply custom marshal names (for re-used shapes that /// marshal differently) /// public class PropertyModifier { public const string EmitPropertyNameKey = "emitPropertyName"; public const string LocationNameKey = "locationName"; public const string AccessModifierKey = "accessModifier"; private readonly string _modelPropertyName; // for debug inspection assist private readonly JsonData _modifierData; internal PropertyModifier(string modelPropertyName, JsonData modifierData) { this._modelPropertyName = modelPropertyName; this._modifierData = modifierData; } // The access modifier for the property. Defaults to public if not set in the customization. public string AccessModifier { get { if (_modifierData[AccessModifierKey] == null) return "public"; return (string)_modifierData[AccessModifierKey]; } } // The user-visible name that should be output for the property public string EmitName { get { if (_modifierData[EmitPropertyNameKey] == null) return null; return (string)_modifierData[EmitPropertyNameKey]; } } // The custom marshal name for the property. If not set, the model // marshal name should be used. public string LocationName { get { if (_modifierData[LocationNameKey] == null) return null; return (string)_modifierData[LocationNameKey]; } } // For properties that are collections, the submember name that // should be used to extract the value when marshalling into the // operation. public string EmitFromMemberName { get { if (_modifierData[EmitFromMemberKey] == null) return null; return (string)_modifierData[EmitFromMemberKey]; } } public bool IsSetUseCustomMarshall { get { return _modifierData[ShapeModifier.CustomMarshallKey] != null; } } // If set, the generated request marshaller will skip the member in // favor of custom marshall logic in the pipeline public bool UseCustomMarshall { get { if (IsSetUseCustomMarshall) return (bool)_modifierData[ShapeModifier.CustomMarshallKey]; return false; } } public string DeprecationMessage { get { return _modifierData[ShapeModifier.DeprecatedMessageKey].CastToString(); } } } #endregion // Injection modifier is an array of objects, each object being the private Dictionary _shapeModifiers = null; // name of the member to add plus any needed shape/marshalling data. // "inject": [ // { "propertyName": // { "type": "list", public Dictionary ShapeModifiers { get { if (_shapeModifiers == null) { _shapeModifiers = new Dictionary(); var data = _documentRoot[ShapeModifier.ShapeModifiersKey]; if (data != null) { foreach (var shapeName in data.PropertyNames) { var modifierData = data[shapeName]; var shapeModifier = new ShapeModifier(modifierData); _shapeModifiers.Add(shapeName, shapeModifier); } } } return _shapeModifiers; } } public ShapeModifier GetShapeModifier(string shapeName) { var shapeModifiers = ShapeModifiers; return shapeModifiers.ContainsKey(shapeName) ? shapeModifiers[shapeName] : null; } /// /// Returns true if the specified property name is excluded at global or /// per-shape scope. /// /// /// /// public bool IsExcludedProperty(string propertyName, string shapeName = null) { var globalShapeModifier = GetShapeModifier("*"); if (globalShapeModifier != null) { if (globalShapeModifier.IsExcludedProperty(propertyName)) return true; } if (shapeName != null) { var shapeModifier = GetShapeModifier(shapeName); if (shapeModifier != null) return shapeModifier.IsExcludedProperty(propertyName); } return false; } /// /// Returns true if the specified property name is marked as requiring backwards compatible handling /// of DateTime values at global or per-shape scope. /// /// /// /// public bool IsBackwardsCompatibleDateTimeProperty(string propertyName, string shapeName = null) { if (shapeName != null) { var shapeModifier = GetShapeModifier(shapeName); if (shapeModifier != null) return shapeModifier.IsBackwardsCompatibleDateTimeProperty(propertyName); } return false; } public OperationModifiers GetOperationModifiers(string operationName) { var data = _documentRoot[OperationModifiers.OperationModifiersKey]; if (data == null) return null; var operation = data[operationName] as JsonData; if (operation == null) return null; var modifiers = new OperationModifiers(); if (operation[OperationModifiers.NameKey] != null && operation[OperationModifiers.NameKey].IsString) modifiers.Name = (string)operation[OperationModifiers.NameKey]; if (operation[OperationModifiers.ExcludeKey] != null && operation[OperationModifiers.ExcludeKey].IsBoolean) modifiers.IsExcluded = (bool)operation[OperationModifiers.ExcludeKey]; if (operation[OperationModifiers.InternalKey] != null && operation[OperationModifiers.InternalKey].IsBoolean) modifiers.IsInternal = (bool)operation[OperationModifiers.InternalKey]; if (operation[OperationModifiers.DeprecatedKey] != null && operation[OperationModifiers.DeprecatedKey].IsBoolean) modifiers.IsDeprecated = (bool)operation[OperationModifiers.DeprecatedKey]; if (operation[OperationModifiers.UseWrappingResultKey] != null && operation[OperationModifiers.UseWrappingResultKey].IsBoolean) modifiers.UseWrappingResult = (bool)operation[OperationModifiers.UseWrappingResultKey]; if (operation[OperationModifiers.AllowEmptyResultKey] != null && operation[OperationModifiers.AllowEmptyResultKey].IsBoolean) modifiers.AllowEmptyResult = (bool)operation[OperationModifiers.AllowEmptyResultKey]; if (operation[OperationModifiers.WrappedResultShapeKey] != null && operation[OperationModifiers.WrappedResultShapeKey].IsString) modifiers.WrappedResultShape = (string)operation[OperationModifiers.WrappedResultShapeKey]; if (operation[OperationModifiers.WrappedResultMemberKey] != null && operation[OperationModifiers.WrappedResultMemberKey].IsString) modifiers.WrappedResultMember = (string)operation[OperationModifiers.WrappedResultMemberKey]; if (operation[OperationModifiers.DocumentationKey] != null && operation[OperationModifiers.DocumentationKey].IsString) modifiers.Documentation = (string)operation[OperationModifiers.DocumentationKey]; if (operation[OperationModifiers.DeprecatedMessageKey] != null && operation[OperationModifiers.DeprecatedMessageKey].IsString) modifiers.DeprecatedMessage = (string)operation[OperationModifiers.DeprecatedMessageKey]; if (operation[OperationModifiers.MarshallNameOverrides] != null && operation[OperationModifiers.MarshallNameOverrides].IsArray) { foreach (JsonData marshalOverride in operation[OperationModifiers.MarshallNameOverrides]) { var shapeName = marshalOverride.PropertyNames.First(); var marshalData = marshalOverride[shapeName]; modifiers.AddMarshallNameOverride(shapeName, marshalData); } } return modifiers; } public JsonData GetExamples(string operationName) { if (_documentRoot.PropertyNames.Contains(ExamplesKey) && _documentRoot[ExamplesKey].IsObject && _documentRoot[ExamplesKey].PropertyNames.Contains(operationName) && _documentRoot[ExamplesKey][operationName].IsArray) return _documentRoot[ExamplesKey][operationName]; var empty = new JsonData(); empty.SetJsonType(JsonType.Array); return empty; } public bool HasExamples { get { return _documentRoot.PropertyNames.Contains(ExamplesKey) && _documentRoot[ExamplesKey].IsObject && _documentRoot[ExamplesKey].PropertyNames.Any(); } } #region Samples Key public class SampleInfo { public const string SolutionPathKey = "solutionPath"; } #endregion #region OperationModifiers /// /// A class used to specify modifiers of a client method /// public class OperationModifiers { public const string OperationModifiersKey = "operationModifiers"; public const string NameKey = "name"; public const string ExcludeKey = "exclude"; public const string InternalKey = "internal"; public const string UseWrappingResultKey = "useWrappingResult"; public const string AllowEmptyResultKey = "allowEmptyResult"; public const string WrappedResultShapeKey = "wrappedResultShape"; public const string WrappedResultMemberKey = "wrappedResultMember"; public const string MarshallNameOverrides = "marshallNameOverrides"; public const string DeprecatedKey = "deprecated"; public const string DeprecatedMessageKey = "deprecatedMessage"; public const string DocumentationKey = "documentation"; // within a marshal override for a shape; one or both may be present public const string MarshallLocationName = "marshallLocationName"; public const string MarshallName = "marshallName"; private Dictionary _marshallNameOverrides = null; /// /// The name of the operation modified /// public string Name { get; set; } /// /// Indicates if the operation should be excluded /// public bool IsExcluded { get; set; } /// /// Indicates if the operation should be marked internal in the client /// public bool IsInternal { get; set; } /// /// Indicates if the operation should be marked deprecated in the client /// public bool IsDeprecated { get; set; } public bool UseWrappingResult { get; set; } /// /// Allows the operation to return an empty result when the operation is modeled to return body result structure. /// public bool AllowEmptyResult { get; set; } public string WrappedResultShape { get; set; } public string WrappedResultMember { get; set; } public string Documentation { get; set; } public string DeprecatedMessage { get; set; } public void AddMarshallNameOverride(string shapeName, JsonData overrides) { if (_marshallNameOverrides == null) _marshallNameOverrides = new Dictionary(); _marshallNameOverrides.Add(shapeName, overrides); } public MarshallNameOverrides GetMarshallNameOverrides(string shapeName, string propertyName) { if (_marshallNameOverrides == null || !_marshallNameOverrides.ContainsKey(shapeName)) return null; var shapeOverrides = _marshallNameOverrides[shapeName][propertyName]; if (shapeOverrides == null) return null; var overrideData = new MarshallNameOverrides(); if (shapeOverrides[MarshallLocationName] != null) overrideData.MarshallLocationName = (string)shapeOverrides[MarshallLocationName]; if (shapeOverrides[MarshallName] != null) overrideData.MarshallName = (string)shapeOverrides[MarshallName]; return overrideData; } } public class ArnFieldProperties { public ArnFieldProperties(bool arnFieldExists, string arnFieldName) { ArnFieldExists = arnFieldExists; ArnFieldName = arnFieldName; } public bool ArnFieldExists { get; set; } public string ArnFieldName { get; set; } } /// /// Contains the naming overrides for a property in a member for marshal /// purposes. This allows shapes that are reused to employ different wire /// names between operations if necessary. /// public class MarshallNameOverrides { public string MarshallName { get; set; } public string MarshallLocationName { get; set; } } #endregion #region DataTypeOverride /// /// A class that represents type overrides for a member /// public class DataTypeOverride { /// /// Creates an override class that specifies overrides for a member /// /// The new custom type for a member /// The custom marshaller for a member /// The custom unmarshaller for a member public DataTypeOverride(string dataType, string marshaller, string unmarshaller) { this.DataType = dataType; this.Marshaller = marshaller; this.Unmarshaller = unmarshaller; } /// /// The new datatype for the property /// public string DataType { get; private set; } /// /// The new marshaller for the property /// public string Marshaller { get; private set; } /// /// The new unmarshaller for the property /// public string Unmarshaller { get; private set; } } #endregion #region RuntimePipelineOverride /// /// A class used to specify overidden runtime details /// public class RuntimePipelineOverride { public List Overrides { get; set; } /// /// Creates a runtime pipeline override that specifies new details about the pipeline /// public RuntimePipelineOverride() { this.Overrides = new List(); } /// /// A class that contains specific override details /// public class Override { public Override() { } /// /// Allows overriding a method in the pipeline /// public string OverrideMethod { get; set; } /// /// Overrides the type in the pipeline /// public string NewType { get; set; } /// /// Overrides the targettype in the pipeline /// public string TargetType { get; set; } /// /// Overrides the constructor input in the pipeline /// public string ConstructorInput { get; set; } /// /// Overrides the condition of the override input in the pipeline. /// A condition on an override allows a customization that is only /// executed when a defined condition is met. The condition must be /// any code that would be valid within the Amazon[ServiceName]Client /// class for the ServiceName where the override is being made. For /// example, a valid condition is: /// "if(this.Config.RetryMode == RequestRetryMode.Legacy)" /// public string Condition { get; set; } /// /// Proceses the override method and provides what type of override should be generated based on the string /// public string FormattedOverrideMethod { get { switch (OverrideMethod) { case "add": return "AddHandler"; case "addAfter": return string.Format(CultureInfo.InvariantCulture, "AddHandlerAfter<{0}>", this.TargetType); case "addBefore": return string.Format(CultureInfo.InvariantCulture, "AddHandlerBefore<{0}>", this.TargetType); case "remove": return string.Format(CultureInfo.InvariantCulture, "RemoveHandler<{0}>", this.TargetType); case "replace": return string.Format(CultureInfo.InvariantCulture, "ReplaceHandler<{0}>", this.TargetType); default: throw new ArgumentException("Invalid override method" + OverrideMethod, "overrideMethod"); } } } } } #endregion } #endregion }