(location, true, out value))
{
throw new Exception("Unknown MarshallLocation " + location);
}
return value;
}
// Default to Body if location is not specified.
return MarshallLocation.Body;
}
}
///
/// The hostLabel of the shape
///
public bool IsHostLabel
{
get
{
if (data[HostLabelKey] != null)
{
return (bool)data[HostLabelKey];
}
// Default to false (not a hostLabel) if hostLabel is not specified.
return false;
}
}
///
/// If present, use instead of MarshallLocationName
///
public string MarshallQueryName
{
get
{
return data[ServiceModel.QueryNameKey] == null ? string.Empty : (string)data[ServiceModel.QueryNameKey];
}
}
///
/// The name of the location for the marshaller
///
public string MarshallLocationName
{
get
{
return data[ServiceModel.LocationNameKey] == null ? string.Empty : (string)data[ServiceModel.LocationNameKey];
}
}
public string GetParamDocumentationForOperation(string operationName)
{
if (this.Documentation.Equals(string.Empty))
{
return "A property of " + operationName + "Request used to execute the " + operationName + " service method.";
}
else
{
var sb = new StringBuilder(this.Documentation);
return sb.Replace("", string.Empty)
.Replace("
", string.Empty)
.Replace("\n", string.Empty)
.Replace(" ", " ")
.ToString();
}
}
public string GetParamDocumentationForConstructor(string className)
{
if (this.Documentation.Equals(string.Empty))
{
return "Sets the " + className + " " + this.PropertyName + " property";
}
else
{
var sb = new StringBuilder(this.Documentation);
return sb.Replace("", string.Empty)
.Replace("
", string.Empty)
.Replace("\n", string.Empty)
.Replace(" ", " ")
.ToString();
}
}
///
/// Used to get the proper type of the member so that it can be used in the generator for proper code
///
/// The proper name of the type for the member
public string DetermineType()
{
return DetermineType(this.data, false);
}
///
/// Determines the type of the member based on customizations, if it isn't custom then it pulls
/// from the full json model to get the shape of this member
///
/// The json data from the parent shape model
/// If true all any found enum type will be returned as a string
///
private string DetermineType(JsonData extendedData, bool treatEnumsAsString)
{
JsonData typeNode = null;
// Check to see if customizations is overriding - first by simple type remap
// and then via more extensive customization. If we are handed a collection
// shape to begin with, this check is whether the collection shape, not the
// collected type, is remapped.
/*var remapAsShapeName = this.model.Customizations.GetSubstituteShapeName(this.Shape.Name);
if (remapAsShapeName != null)
{
var remappedShape = this.model.FindShape(remapAsShapeName);
if (remappedShape == null)
throw new Exception(string.Format("Found shape remap from {0} to {1} but no such shape in model!",
this.Shape.Name, remapAsShapeName));
if (remappedShape.IsStructure)
return remapAsShapeName;
switch (remappedShape.Type)
{
case "boolean":
return "bool";
case "integer":
return "int";
case "timestamp":
return "DateTime";
default:
return remappedShape.Type;
}
}*/
var overrideType = this.model.Customizations.OverrideDataType(OwningShape.Name, this._name);
if (overrideType != null)
{
this._newType = overrideType.DataType;
return overrideType.DataType;
}
var extendsNode = extendedData[ServiceModel.ShapeKey];
if (extendsNode == null)
throw new Exception("Missing extends for member " + this._name);
JsonData memberShape = null;
// if this shape is a collection, has the collected type been remapped?
var emitAsShapeName = this.model.Customizations.GetSubstituteShapeName(extendsNode.ToString());
var renameShape = this.model.Customizations.GetOverrideShapeName(extendsNode.ToString());
if (emitAsShapeName == null)
{
memberShape = this.model.DocumentRoot[ServiceModel.ShapesKey][extendsNode.ToString()];
typeNode = memberShape[Shape.TypeKey];
}
else
{
// we only handle remap to one level at present
memberShape = this.model.DocumentRoot[ServiceModel.ShapesKey][emitAsShapeName];
typeNode = memberShape[Shape.TypeKey];
}
var documentTrait = memberShape[Shape.DocumentKey];
if (documentTrait?.IsBoolean == true && (bool) documentTrait)
return "Amazon.Runtime.Documents.Document";
if (typeNode == null)
throw new Exception("Type is missing for shape " + extendsNode.ToString());
switch (typeNode.ToString())
{
case "string":
if (!treatEnumsAsString && memberShape["enum"] != null)
{
return (renameShape ?? extendsNode.ToString()).ToUpperFirstCharacter();
}
return "string";
case "blob":
if (this.IsStreaming)
return "Stream";
return "MemoryStream";
case "boolean":
return "bool";
case "double":
return "double";
case "float":
return "float";
case "integer":
return "int";
case "long":
return "long";
case "timestamp":
return "DateTime";
case "structure":
return emitAsShapeName ?? renameShape ?? extendsNode.ToString();
case "map":
var keyType = DetermineType(memberShape["key"], true);
var valueType = DetermineType(memberShape["value"], true);
return string.Format("Dictionary<{0}, {1}>", keyType, valueType);
case "list":
var listType = DetermineType(memberShape["member"], true);
return string.Format("List<{0}>", listType);
case "decimal":
throw new Exception(UnhandledTypeDecimalErrorMessage);
default:
throw new Exception("Unknown type " + typeNode.ToString());
}
}
///
/// Returns the type of the marshaller if it is customized, null otherwise
///
public string CustomMarshallerTransformation
{
get
{
// Check to see if customizations is overriding.
var overrideType = this.model.Customizations.OverrideDataType(OwningShape.Name, this._name);
if (overrideType != null)
return overrideType.Marshaller;
return null;
}
}
///
/// This method gets the name of an unmarshaller for a type. It is used from within DetermineTypeUnmarshallerInstantiate
/// to get the types for the generic declarations of maps and lists.
///
/// The data for the member defined in the shape
///
string GetTypeUnmarshallerName(JsonData extendedData)
{
// Check to see if customizations is overriding.
var overrideType = this.model.Customizations.OverrideDataType(OwningShape.Name, this._name);
if (overrideType != null)
return overrideType.Unmarshaller;
var extendsNode = extendedData[ServiceModel.ShapeKey];
if (extendsNode == null)
throw new Exception("Missing extends for member " + this._name);
var memberShape = this.model.DocumentRoot[ServiceModel.ShapesKey][extendsNode.ToString()];
var document = memberShape[Shape.DocumentKey];
if (document?.IsBoolean == true && (bool)document)
return "Amazon.Runtime.Documents.Internal.Transform.DocumentUnmarshaller";
var typeNode = memberShape[Shape.TypeKey];
if (typeNode == null)
throw new Exception("Type is missing for shape " + extendsNode.ToString());
switch (typeNode.ToString())
{
case "string":
return "StringUnmarshaller";
case "blob":
return "MemoryStreamUnmarshaller";
case "boolean":
return "BoolUnmarshaller";
case "double":
return "DoubleUnmarshaller";
case "float":
return "FloatUnmarshaller";
case "integer":
return "IntUnmarshaller";
case "long":
return "LongUnmarshaller";
case "timestamp":
return "DateTimeUnmarshaller";
case "structure":
var shapeName = extendsNode.ToString();
var renamedShape = this.model.Customizations.GetOverrideShapeName(shapeName);
var substitutedShape = this.model.Customizations.GetSubstituteShapeName(shapeName);
if (!string.IsNullOrWhiteSpace(renamedShape))
{
shapeName = renamedShape;
}
else if(!string.IsNullOrWhiteSpace(substitutedShape))
{
shapeName = substitutedShape;
}
return shapeName + "Unmarshaller";
case "map":
var keyType = DetermineType(memberShape[Shape.KeyKey], true);
var keyTypeUnmarshaller = GetTypeUnmarshallerName(memberShape[Shape.KeyKey]);
var valueType = DetermineType(memberShape[Shape.ValueKey], true);
var valueTypeUnmarshaller = GetTypeUnmarshallerName(memberShape[Shape.ValueKey]);
return string.Format("DictionaryUnmarshaller<{0}, {1}, {2}, {3}>",
keyType, valueType, keyTypeUnmarshaller, valueTypeUnmarshaller);
case "list":
var listType = DetermineType(memberShape[Member.MemberKey], true);
var listTypeUnmarshaller = GetTypeUnmarshallerName(memberShape[Member.MemberKey]);
return string.Format("ListUnmarshaller<{0}, {1}>",
listType, listTypeUnmarshaller);
case "decimal":
throw new Exception(UnhandledTypeDecimalErrorMessage);
default:
throw new Exception("Unknown type " + typeNode.ToString());
}
}
///
/// Determines the type of the unmarshaller for the member
///
/// A string that can be used as a proper name for the unmarshaller
public string DetermineTypeUnmarshallerInstantiate()
{
return this.DetermineTypeUnmarshallerInstantiate(this.data);
}
///
/// This returns the code that instantiates an unmarshaller. It will be called recursively for maps and
/// lists to handle the generic declarations.
///
///
///
public string DetermineTypeUnmarshallerInstantiate(JsonData extendedData)
{
// Check to see if customizations is overriding.
var overrideType = this.model.Customizations.OverrideDataType(OwningShape.Name, this._name);
if (overrideType != null && !string.IsNullOrEmpty(overrideType.Unmarshaller))
return overrideType.Unmarshaller + ".Instance";
var extendsNode = extendedData[ServiceModel.ShapeKey];
if (extendsNode == null)
throw new Exception("Missing extends for member " + this._name);
JsonData memberShape = null;
var substituteType = this.model.Customizations.GetSubstituteShapeName(extendsNode.ToString());
var renameShape = this.model.Customizations.GetOverrideShapeName(extendsNode.ToString());
memberShape = substituteType != null
? this.model.DocumentRoot[ServiceModel.ShapesKey][substituteType]
: this.model.DocumentRoot[ServiceModel.ShapesKey][extendsNode.ToString()];
var document = memberShape[Shape.DocumentKey];
if (document?.IsBoolean == true && (bool) document)
return "Amazon.Runtime.Documents.Internal.Transform.DocumentUnmarshaller.Instance";
var typeNode = memberShape[Shape.TypeKey];
if (typeNode == null)
throw new Exception("Type is missing for shape " + extendsNode);
switch (typeNode.ToString())
{
case "string":
return "StringUnmarshaller.Instance";
case "blob":
return "MemoryStreamUnmarshaller.Instance";
case "boolean":
return "BoolUnmarshaller.Instance";
case "double":
return "DoubleUnmarshaller.Instance";
case "float":
return "FloatUnmarshaller.Instance";
case "integer":
if (this.UseNullable)
return "NullableIntUnmarshaller.Instance";
return "IntUnmarshaller.Instance";
case "long":
return "LongUnmarshaller.Instance";
case "timestamp":
if (this.UseNullable)
return "NullableDateTimeUnmarshaller.Instance";
return "DateTimeUnmarshaller.Instance";
case "structure":
return (renameShape ?? extendsNode) + "Unmarshaller.Instance";
case "map":
var keyType = DetermineType(memberShape[Shape.KeyKey], true);
var keyTypeUnmarshaller = GetTypeUnmarshallerName(memberShape[Shape.KeyKey]);
var keyTypeUnmarshallerInstantiate = DetermineTypeUnmarshallerInstantiate(memberShape[Shape.KeyKey]);
var valueType = DetermineType(memberShape[Shape.ValueKey], true);
var valueTypeUnmarshaller = GetTypeUnmarshallerName(memberShape[Shape.ValueKey]);
var valueTypeUnmarshallerInstantiate = DetermineTypeUnmarshallerInstantiate(memberShape[Shape.ValueKey]);
if (this.model.Type == ServiceType.Json || this.model.Type == ServiceType.Rest_Json || this.model.Type == ServiceType.Rest_Xml)
return string.Format("new DictionaryUnmarshaller<{0}, {1}, {2}, {3}>(StringUnmarshaller.Instance, {5})",
keyType, valueType, keyTypeUnmarshaller, valueTypeUnmarshaller, keyTypeUnmarshallerInstantiate, valueTypeUnmarshallerInstantiate);
else
return string.Format("new KeyValueUnmarshaller<{0}, {1}, {2}, {3}>(StringUnmarshaller.Instance, {5})",
keyType, valueType, keyTypeUnmarshaller, valueTypeUnmarshaller, keyTypeUnmarshallerInstantiate, valueTypeUnmarshallerInstantiate);
case "list":
var listType = DetermineType(memberShape[Shape.MemberKey], true);
var listTypeUnmarshaller = GetTypeUnmarshallerName(memberShape[Shape.MemberKey]);
var listTypeUnmarshallerInstantiate = DetermineTypeUnmarshallerInstantiate(memberShape[Shape.MemberKey]);
if (this.model.Type == ServiceType.Json || this.model.Type == ServiceType.Rest_Json)
return string.Format("new ListUnmarshaller<{0}, {1}>({2})",
listType, listTypeUnmarshaller, listTypeUnmarshallerInstantiate);
else
return listTypeUnmarshallerInstantiate;
case "decimal":
throw new Exception(UnhandledTypeDecimalErrorMessage);
default:
throw new Exception("Unknown type " + typeNode.ToString());
}
}
public string GetPrimitiveType()
{
return this.Shape.GetPrimitiveType();
}
///
/// Determines if they type of the member is a primitive that needs to be nullable.
/// If the shape is customized it uses the newtype to determine if it is nullable.
///
public bool IsNullable
{
get
{
var emittingShapeType = this._newType; // set if we have a full override declaration
if (emittingShapeType == null)
{
// maybe a simple substitution?
var substituteData = this.model.Customizations.GetSubstituteShapeData(this.ModelShape.Name);
if (substituteData != null && substituteData[CustomizationsModel.EmitAsShapeKey] != null)
{
var shape = this.model.FindShape((string) substituteData[CustomizationsModel.EmitAsShapeKey]);
emittingShapeType = shape.Type;
}
}
return emittingShapeType != null
? Shape.NullableTypes.Contains(emittingShapeType, StringComparer.Ordinal)
: this.Shape.IsNullable;
}
}
///
/// Determines if the operation is Idempotent.
///
public bool IsIdempotent
{
get
{
var source = data[ServiceModel.IdempotencyTokenKey];
if (source != null && source.IsBoolean)
return (bool)source;
return false;
}
}
///
/// Determines if the member is a map from the shape in the json model
///
public bool IsMap
{
get
{
return this.Shape.IsMap;
}
}
///
/// Determines if the member is a list from the shape in the json model
///
public bool IsList
{
get
{
return this.Shape.IsList;
}
}
///
/// Determines if the member is a structure from the shape in the json model
///
public bool IsStructure
{
get
{
return this.Shape.IsStructure;
}
}
///
/// Determines if the member is a memorystream from the shape in the json model
///
public bool IsMemoryStream
{
get
{
return this.Shape.IsMemoryStream;
}
}
///
/// Determines if the member has requiresLength from the shape in the json model
///
public bool RequiresLength
{
get
{
return this.Shape.RequiresLength;
}
}
///
/// Determines if the member is a stream from the shape in the json model
///
public bool IsStreaming
{
get
{
return this.Shape.IsStreaming;
}
}
///
/// Determines if the member is a document from the shape in the json model
///
public bool IsDocument
{
get
{
return this.Shape.IsDocument;
}
}
///
/// Determines if the member is deprecated
///
public bool IsDeprecated
{
get
{
if (data[DeprecatedKey] != null && data[DeprecatedKey].IsBoolean)
return (bool)data[DeprecatedKey];
return false;
}
}
///
/// Determines if the member is required
///
public bool IsRequired
{
get
{
return OwningShape.IsFieldRequired(ModeledName);
}
}
///
/// Returns the deprecation message specified in the model or in the customization file.
///
public string DeprecationMessage
{
get
{
string message = this.model.Customizations.GetPropertyModifier(this.OwningShape.Name, this._name)?.DeprecationMessage ??
this.data[DeprecatedMessageKey].CastToString();
if (message == null)
throw new Exception(string.Format("The 'message' property of the 'deprecated' trait is missing for member {0}.{1}.\nFor example: \"MemberName\":{{ ... \"deprecated\":true, \"deprecatedMessage\":\"This property is deprecated, use XXX instead.\"}}", this.OwningShape.Name, this._name));
return message;
}
}
public bool IsExcluded
{
get { return this.model.Customizations.IsExcludedProperty(this.BasePropertyName, this.OwningShape.Name); }
}
///
/// Determines if the member is an event payload type
///
public bool IsEventPayload
{
get
{
if (data[EventPayloadKey] != null && data[EventPayloadKey].IsBoolean)
return (bool)data[EventPayloadKey];
return false;
}
}
public bool IsBackwardsCompatibleDateTimeProperty
{
get { return this.model.Customizations.IsBackwardsCompatibleDateTimeProperty(this.BasePropertyName, this.OwningShape.Name); }
}
///
/// Determines if the member is a type that needs to be instantiated, such as a list or map
///
public bool ShouldInstantiate
{
get { return this.IsMap || this.IsList; }
}
///
/// Gets the name of the shape that the member extends so that info about the member can be retrieved
///
string Extends
{
get
{
var extendsNode = this.data[ServiceModel.ShapeKey];
if (extendsNode == null)
throw new Exception("Missing extends for member " + this._name);
return extendsNode.ToString();
}
}
// Returns the original model shape
public Shape ModelShape
{
get
{
var memberShape = this.model.DocumentRoot[ServiceModel.ShapesKey][this.Extends];
return Shape.CreateShape(this.model, this.Extends, memberShape);
}
}
// Returns the Shape, potentially overriden by substitution, that we want to
// deal with
public Shape Shape
{
get
{
JsonData memberShape = null;
var substituteType = this.model.Customizations.GetSubstituteShapeName(this.Extends);
memberShape = substituteType != null
? this.model.DocumentRoot[ServiceModel.ShapesKey][substituteType]
: this.model.DocumentRoot[ServiceModel.ShapesKey][this.Extends];
if (memberShape == null)
throw new Exception("Type is missing for shape " + this.Extends);
return Shape.CreateShape(this.model, this.Extends, memberShape);
}
}
///
/// Returns the name of this member as it appears in the model
///
public string ModeledName
{
get
{
return this._name;
}
}
///
/// TimestampFormat that may be specified on a member or a shape.
///
public TimestampFormat TimestampFormat
{
get
{
if (!this.IsDateTime)
{
throw new InvalidOperationException(string.Format(
CultureInfo.InvariantCulture,
"Property TimestampFormat is not valid for member {0} of type {1}.",
this.ModeledName, this.DetermineType()));
}
var resolvedTimestampFormat = data.GetTimestampFormat();
if (resolvedTimestampFormat == TimestampFormat.None)
{
// Fallback to shape's TimestampFormat if not specified at member level
// Fallback to marshall location/protocol rules if not specified at shape level
resolvedTimestampFormat = this.Shape.GetTimestampFormat(this.MarshallLocation);
}
return resolvedTimestampFormat;
}
}
///
/// Returns if the member's type is timestamp.
///
public bool IsDateTime
{
get
{
return this.DetermineType().Equals("DateTime", StringComparison.InvariantCulture);
}
}
///
/// Returns the marshaller method to use in the generated marshaller code for a
/// member of primitive type.
///
public string PrimitiveMarshaller
{
get
{
if (this.IsDateTime)
{
return "StringUtils.FromDateTimeTo" + this.TimestampFormat;
}
else
{
return "StringUtils.From" + this.GetPrimitiveType();
}
}
}
///
/// Creates a representation of the member as a string using the member name
///
/// The member name as a string
public override string ToString()
{
return this._name;
}
internal static TimestampFormat GetDefaultTimestampFormat(MarshallLocation marshallLocation, ServiceType serviceType)
{
// Rules used to default the format if timestampFormat is not specified.
// 1. All timestamp values serialized in HTTP headers are formatted using rfc822 by default.
// 2. All timestamp values serialized in query strings are formatted using iso8601 by default.
if (marshallLocation == MarshallLocation.Header)
{
return TimestampFormat.RFC822;
}
else if (marshallLocation == MarshallLocation.QueryString)
{
return TimestampFormat.ISO8601;
}
else
{
// Return protocol defaults if marshall location is not header or querystring.
// The default timestamp formats per protocol for structured payload shapes are as follows.
// rest-json: unixTimestamp
// jsonrpc: unixTimestamp
// rest-xml: iso8601
// query: iso8601
// ec2: iso8601
switch (serviceType)
{
case ServiceType.Rest_Json:
return TimestampFormat.UnixTimestamp;
case ServiceType.Json:
return TimestampFormat.UnixTimestamp;
case ServiceType.Query:
return TimestampFormat.ISO8601;
case ServiceType.Rest_Xml:
return TimestampFormat.ISO8601;
default:
throw new InvalidOperationException(
"Encountered unknown model type (protocol): " + serviceType);
}
}
}
///
/// Gets request member context parameter, used to drive endpoint resolution
///
public ContextParameter ContextParameter
{
get
{
var parameter = data.SafeGet("contextParam");
return parameter == null ? null : new ContextParameter { name = parameter.SafeGetString("name") };
}
}
}
}