/// <summary> /// Get the Objective C representation of an enum type from a string hint and the odata path segment /// </summary> /// <param name="enumHint">string representing the hint to use for enum lookup</param> /// <param name="pathSegment">Odata Function/Entity from which the object is needed</param> /// <param name="path">List of strings/identifier showing the path through the Edm/json structure to reach the Class Identifier from the segment</param> private static string GenerateEnumString(string enumHint, ODataPathSegment pathSegment, ICollection <string> path) { IEdmType nestEdmType; try { nestEdmType = CommonGenerator.GetEdmTypeFromIdentifier(pathSegment, path); } catch (Exception) { return(string.Empty); } if (nestEdmType is IEdmEnumType edmEnumType) { var typeName = GetObjectiveCModelName(pathSegment, path); var temp = enumHint.Split(", ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries).First(); //look for the proper name of the enum in the members foreach (var member in edmEnumType.Members) { if (temp.Equals(member.Name, StringComparison.OrdinalIgnoreCase)) { return($"{typeName} {member.Name}"); } } //if search failed default to first element return($"{typeName} {edmEnumType.Members.First().Name}"); } //not an enum return(string.Empty); }
/// <summary> /// Java specific function to generate the Enum string representation of an enum /// </summary> /// <param name="enumHint">Name of the enum needing to change to java fashion(typically camel cased)</param> /// <param name="pathSegment">Odata path segment containing needed parent information to know the owner of the enum</param> /// <param name="path">List of string showing the path of traversal through the tree structure</param> /// <returns>Java representation of an enum type e.g. Importance.LOW</returns> private static string GenerateEnumString(string enumHint, ODataPathSegment pathSegment, List <string> path) { IEdmType nestEdmType; try { nestEdmType = CommonGenerator.GetEdmTypeFromIdentifier(pathSegment, path); } catch (Exception) { return(string.Empty); } if (nestEdmType is IEdmEnumType edmEnumType) { var typeName = GetJavaClassNameFromOdataPath(pathSegment, path); var temp = string.IsNullOrEmpty(enumHint) ? string.Empty : enumHint.Split(", ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries).First();//split in case we need to 'or' the members //look for the proper name of the enum in the members foreach (var member in edmEnumType.Members) { if (temp.Equals(member.Name, StringComparison.OrdinalIgnoreCase)) { return($"{typeName}.{ GetSnakeCaseFromCamelCase(member.Name) }"); } } //return the enum type in uppercase java fashion return($"{typeName}.{ GetSnakeCaseFromCamelCase(edmEnumType.Members.First().Name) }"); } return(string.Empty); }
/// <summary> /// Return string representation of the classname for Java /// </summary> /// <param name="pathSegment">The OdataPathSegment in use</param> /// <param name="path">Path to follow to get find the classname</param> /// <returns>String representing the type in use</returns> private static string GetJavaClassNameFromOdataPath(ODataPathSegment pathSegment, List <string> path) { var edmType = CommonGenerator.GetEdmTypeFromIdentifier(pathSegment, path); //we need to split the string and get last item //eg microsoft.graph.data => Data return(CommonGenerator.UppercaseFirstLetter(edmType.ToString().Split(".").Last())); }
/// <summary> /// Return string representation of the classname for Objective C /// </summary> /// <param name="pathSegment">The OdataPathSegment in use</param> /// <param name="path">Path to follow to get find the classname</param> /// <returns>String representing the type in use</returns> private static string GetObjectiveCModelName(ODataPathSegment pathSegment, ICollection <string> path) { var edmType = CommonGenerator.GetEdmTypeFromIdentifier(pathSegment, path); //we need to split the string and get last item //eg microsoft.graph.data => MSGraphData return("MSGraph" + CommonGenerator.UppercaseFirstLetter(edmType.ToString().Split(".").Last())); }
/// <summary> /// Java specific function to check how many times a variableName has been used and append a number at the end to make it unique /// </summary> /// <param name="variableName">Variable name to be used</param> /// <param name="usedList">List of variable names that have been already used</param> private static string EnsureJavaVariableNameIsUnique(string variableName, IEnumerable <string> usedList) { var count = usedList.Count(x => x.Equals(variableName)); if (count > 0) { return($"{variableName}{count}"); //append the count to the end of string since we've used it before } return(CommonGenerator.EnsureVariableNameIsNotReserved(variableName, new JavaExpressions())); //make sure its not reserved }
/// <summary> /// Helper function that takes in a camel case to return a snake case string in all caps for java enums /// </summary> /// <param name="camelCaseString">String that is in camel case</param> /// <returns>Snake case string in all caps e.g myBad => MY_BAD</returns> private static string GetSnakeCaseFromCamelCase(string camelCaseString) { var snakeCaseBuilder = new StringBuilder(); foreach (var c in CommonGenerator.LowerCaseFirstLetter(camelCaseString)) { if (char.IsUpper(c)) { snakeCaseBuilder.Append("_"); //insert underscore between words } snakeCaseBuilder.Append(char.ToUpper(c)); //convert the character to uppercase } return(snakeCaseBuilder.ToString()); }
/// <summary> /// Generate the snippet section for the Property segment for CSharp code as this section cannot be directly accessed in \ /// a URL fashion /// </summary> /// <param name="snippetModel">Snippet model built from the request</param> private static string GeneratePropertySectionSnippet(SnippetModel snippetModel) { if (snippetModel.Segments.Count < 2) { return(""); } var stringBuilder = new StringBuilder(); var desiredSegment = snippetModel.Segments.Last(); var segmentIndex = snippetModel.Segments.Count - 1; //move back to find first segment that is not a PropertySegment while ((desiredSegment is PropertySegment) && (segmentIndex > 0)) { desiredSegment = snippetModel.Segments[--segmentIndex]; } var selectField = snippetModel.Segments[segmentIndex + 1].Identifier; //generate path to the property var properties = ""; while (segmentIndex < snippetModel.Segments.Count - 1) { properties = properties + "." + CommonGenerator.UppercaseFirstLetter(snippetModel.Segments[++segmentIndex].Identifier); } //modify the responseVarible name snippetModel.ResponseVariableName = desiredSegment.Identifier; var variableName = snippetModel.Segments.Last().Identifier; var parentClassName = GetCsharpClassName(desiredSegment, new List <string> { snippetModel.ResponseVariableName }); if (snippetModel.Method == HttpMethod.Get) { //we are retrieving the value snippetModel.SelectFieldList.Add(selectField); stringBuilder.Append($"\n\nvar {variableName} = {snippetModel.ResponseVariableName}{properties};"); } else { //we are modifying the value stringBuilder.Append($"var {snippetModel.ResponseVariableName} = new {parentClassName}();");//initialise the classname stringBuilder.Append($"\n{snippetModel.ResponseVariableName}{properties} = {variableName};\n\n"); } return(stringBuilder.ToString()); }
/// <summary> /// Generates language specific code to add special properties to the AdditionalData dictionary /// </summary> /// <param name="stringBuilder">The original string builder containing code generated so far</param> /// <param name="key">The odata key/property</param> /// <param name="value">The value related to the odata key/property</param> /// <param name="className">The class name for the entity needing modification</param> /// <param name="tabSpace">Tab space to use for formatting the code generated</param> /// <returns>a string builder with the relevant odata code added</returns> private static StringBuilder GenerateCSharpAdditionalDataSection(StringBuilder stringBuilder, string key, string value, string className, string tabSpace) { switch (key) { case "@odata.id" when className.Equals("DirectoryObject"): try { var uriLastSegmentString = new Uri(value).Segments.Last(); uriLastSegmentString = Uri.UnescapeDataString(uriLastSegmentString); stringBuilder.Append($"{tabSpace}\tId = \"{uriLastSegmentString}\",\r\n"); } catch (UriFormatException) { stringBuilder.Append($"{tabSpace}\tId = \"{value}\",\r\n"); //its not really a URI } break; case "@odata.type": var proposedType = CommonGenerator.UppercaseFirstLetter(value.Split(".").Last()); //check if the odata type specified is different // maybe due to the declaration of a subclass of the type specified from the url. if (!className.Equals(proposedType)) { stringBuilder.Replace(className, proposedType); } break; default: //just append the property as part of the additionalData of the object var additionalDataString = $"{tabSpace}\tAdditionalData = new Dictionary<string, object>()\r\n{tabSpace}\t{{\r\n"; var keyValuePairElement = $"{tabSpace}\t\t{{\"{key}\",\"{value}\"}}"; if (!stringBuilder.ToString().Contains(additionalDataString)) //check if we ever inserted AdditionalData to this object. { stringBuilder.Append($"{additionalDataString}{keyValuePairElement}\r\n{tabSpace}\t}},\r\n"); } else { //insert new key value pair to already existing AdditionalData component var insertionIndex = stringBuilder.ToString().IndexOf(additionalDataString, StringComparison.Ordinal) + additionalDataString.Length; stringBuilder.Insert(insertionIndex, $"{keyValuePairElement},\r\n"); } break; } return(stringBuilder); }
/// <summary> /// Get the Csharp representation of an enum type from a string hint and the odata path segment /// </summary> /// <param name="enumHint">string representing the hint to use for enum lookup</param> /// <param name="pathSegment">Odata Function/Entity from which the object is needed</param> /// <param name="path">List of strings/identifier showing the path through the Edm/json structure to reach the Class Identifier from the segment</param> private static string GenerateEnumString(string enumHint, ODataPathSegment pathSegment, ICollection <string> path) { IEdmType nestEdmType; try { nestEdmType = CommonGenerator.GetEdmTypeFromIdentifier(pathSegment, path); } catch (Exception) { return(string.Empty); } if (nestEdmType is IEdmEnumType edmEnumType) { var typeName = GetCsharpClassName(pathSegment, path); var temp = enumHint.Split(", ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);//split incase we need to 'or' the members var enumStringList = new List <string>(); //look for the proper name of the enum in the members foreach (var member in edmEnumType.Members) { if (temp.Contains(member.Name, StringComparer.OrdinalIgnoreCase)) { enumStringList.Add($"{typeName}.{CommonGenerator.UppercaseFirstLetter(member.Name)}"); } } //if search failed default to first element if (!enumStringList.Any()) { enumStringList.Add($"{typeName}.{CommonGenerator.UppercaseFirstLetter(edmEnumType.Members.First().Name)}"); } //return the enum type "ORed" together return(CommonGenerator.GetListAsStringForSnippet(enumStringList, " | ")); } return(string.Empty); }
/// <summary> /// Generates language specific code to add special properties to the AdditionalData dictionary /// </summary> /// <param name="stringBuilder">The original string builder containing code generated so far</param> /// <param name="key">The odata key/property</param> /// <param name="value">The value related to the odata key/property</param> /// <param name="className">The class name for the entity needing modification</param> /// <param name="currentVarName">The variable name of the current object in use </param> /// <returns>a string builder with the relevant odata code added</returns> private static StringBuilder GenerateJavaAdditionalDataSection(StringBuilder stringBuilder, string key, string value, string className, string currentVarName) { switch (key) { case "@odata.id" when className.Equals("DirectoryObject"): try { var uriLastSegmentString = new Uri(value).Segments.Last(); uriLastSegmentString = Uri.UnescapeDataString(uriLastSegmentString); stringBuilder.Append($"{currentVarName}.Id = \"{uriLastSegmentString}\";\r\n"); } catch (UriFormatException) { stringBuilder.Append($"{currentVarName}.Id = \"{value}\";\r\n"); //its not really a URI } break; case "@odata.type": var proposedType = CommonGenerator.UppercaseFirstLetter(value.Split(".").Last()); // check if the odata type specified is different // maybe due to the declaration of a subclass of the type specified from the url. if (!className.Equals(proposedType)) { stringBuilder.Replace(className, proposedType); } break; default: //just append the property as part of the additionalData of the object stringBuilder.Append($"{currentVarName}.additionalDataManager().put(\"{key}\", new JsonPrimitive(\"{value}\"));\r\n"); break; } return(stringBuilder); }
/// <summary> /// Java specific function that infers the return type for java that is going to be returned /// </summary> /// <param name="edmType">Definition of the type from the OData model</param> /// <param name="typeHint">Hint of the variable name that is coming back</param> /// <returns>String representing the return type</returns> private static string GetJavaReturnTypeName(IEdmType edmType, string typeHint) { var typeName = string.Empty; if (edmType is IEdmCollectionType collectionType) { if (collectionType.ElementType.Definition is IEdmNamedElement edmNamedElement) { typeName = typeHint.Equals("delta", StringComparison.OrdinalIgnoreCase) ? $"I{CommonGenerator.UppercaseFirstLetter(edmNamedElement.Name)}DeltaCollectionPage" : $"I{CommonGenerator.UppercaseFirstLetter(edmNamedElement.Name)}CollectionPage"; } else { typeName = $"I{typeName}CollectionPage"; } } else { typeName = edmType == null ? "Content": CommonGenerator.UppercaseFirstLetter(edmType.FullTypeName().Split(".").Last()); } return(typeName); }
/// <summary> /// CSharpGenerator constructor /// </summary> /// <param name="model">Model representing metadata</param> public CSharpGenerator(IEdmModel model) { CommonGenerator = new CommonGenerator(model); }
/// <summary> /// Csharp function to generate Object constructor section of a code snippet. In the event that another object is needed in the middle of generation, /// a recursive call is made to sort out the needed object. /// </summary> /// <param name="pathSegment">Odata Function/Entity from which the object is needed</param> /// <param name="jsonBody">Json string from which the information of the object to be initialized is held</param> /// <param name="path">List of strings/identifier showing the path through the Edm/json structure to reach the Class Identifier from the segment</param> private static string CSharpGenerateObjectFromJson(ODataPathSegment pathSegment, string jsonBody, ICollection <string> path) { var stringBuilder = new StringBuilder(); var jsonObject = JsonConvert.DeserializeObject(jsonBody); var tabSpace = new string('\t', path.Count - 1);//d switch (jsonObject) { case string _: { var enumString = GenerateEnumString(jsonObject.ToString(), pathSegment, path); if (!string.IsNullOrEmpty(enumString)) { //Enum is accessed as the Classname then enum type e.g Importance.Low stringBuilder.Append($"{tabSpace}{enumString}\r\n"); } else if (jsonObject.Equals("true") || jsonObject.Equals("false")) { stringBuilder.Append($"{tabSpace}{jsonObject}\r\n"); //boolean primitives values masquerading as strings. } else { stringBuilder.Append($"{tabSpace}{GenerateSpecialClassString($"{jsonObject}", pathSegment, path)}"); } } break; case JObject jObject: { var className = GetCsharpClassName(pathSegment, path); stringBuilder.Append($"new {className}\r\n"); stringBuilder.Append($"{tabSpace}{{\r\n"); //opening curly brace //initialize each member/property of the object foreach (var(key, jToken) in jObject) { var value = JsonConvert.SerializeObject(jToken); var newPath = path.Append(key).ToList(); //add new identifier to the path if (key.Contains("@odata") || key.StartsWith("@")) //sometimes @odata maybe in the middle e.g."*****@*****.**" { stringBuilder = GenerateCSharpAdditionalDataSection(stringBuilder, key, jToken.Value <string>(), className, tabSpace); continue; } switch (jToken.Type) { case JTokenType.Array: case JTokenType.Object: //new nested object needs to be constructed so call this function recursively to make it var newObject = CSharpGenerateObjectFromJson(pathSegment, value, newPath); stringBuilder.Append($"{tabSpace}\t{CommonGenerator.UppercaseFirstLetter(key)} = {newObject}".TrimEnd() + ",\r\n"); break; default: // we can call the function recursively to handle the other states of string/enum/special classes stringBuilder.Append($"{tabSpace}\t{CommonGenerator.UppercaseFirstLetter(key)} = {CSharpGenerateObjectFromJson(pathSegment, value, newPath).Trim()},\r\n"); break; } } //remove the trailing comma if we appended anything if (stringBuilder[stringBuilder.Length - 3].Equals(',')) { stringBuilder.Remove(stringBuilder.Length - 3, 1); } //closing brace stringBuilder.Append($"{tabSpace}}}\r\n"); } break; case JArray array: { var objectList = array.Children <JObject>(); var className = GetCsharpClassName(pathSegment, path); stringBuilder.Append($"new List<{className}>()\r\n{tabSpace}{{\r\n"); if (objectList.Any()) { foreach (var item in objectList) { var jsonString = JsonConvert.SerializeObject(item); //we need to create a new object var objectStringFromJson = CSharpGenerateObjectFromJson(pathSegment, jsonString, path).TrimEnd(";\r\n".ToCharArray()); //indent it one tab level then append it to the string builder objectStringFromJson = $"{tabSpace}\t{objectStringFromJson.Replace("\r\n", "\r\n\t")}"; stringBuilder.Append($"{objectStringFromJson},\r\n"); } stringBuilder.Remove(stringBuilder.Length - 3, 1); //remove the trailing comma } else //we don't have object nested but something else like empty list/strings/enums { foreach (var element in array) { var listItem = CSharpGenerateObjectFromJson(pathSegment, JsonConvert.SerializeObject(element), path).TrimEnd(";\r\n".ToCharArray()); stringBuilder.Append($"{tabSpace}\t{listItem.TrimStart()},\r\n"); } //remove the trailing comma if we appended anything if (stringBuilder[stringBuilder.Length - 3].Equals(',')) { stringBuilder.Remove(stringBuilder.Length - 3, 1); } } stringBuilder.Append($"{tabSpace}}}\r\n"); } break; case DateTime _: stringBuilder.Append($"{tabSpace}{GenerateSpecialClassString(jsonBody.Replace("\"",""), pathSegment, path)}"); break; case null: stringBuilder.Append($"{tabSpace}null"); break; default: var primitive = jsonObject.ToString(); //json deserializer capitalizes the bool types so undo that if (primitive.Equals("True", StringComparison.Ordinal) || primitive.Equals("False", StringComparison.Ordinal)) { primitive = CommonGenerator.LowerCaseFirstLetter(primitive); } //item is a primitive print as is stringBuilder.Append($"{tabSpace}{primitive}\r\n"); break; } //check if this is the outermost object in a potential nested object structure and needs the semicolon termination character. return(path.Count == 1 ? $"{stringBuilder.ToString().TrimEnd()};\r\n\r\n" : stringBuilder.ToString()); }
/// <summary> /// Formulates the requested Graph snippets and returns it as string for Csharp /// </summary> /// <param name="snippetModel">Model of the Snippets info <see cref="SnippetModel"/></param> /// <param name="languageExpressions">The language expressions to be used for code Gen</param> /// <returns>String of the snippet in Csharp code</returns> public static string GenerateCodeSnippet(SnippetModel snippetModel, LanguageExpressions languageExpressions) { var snippetBuilder = new StringBuilder(); try { snippetBuilder.Append("GraphServiceClient graphClient = new GraphServiceClient( authProvider );\r\n\r\n"); var segment = snippetModel.Segments.Last(); snippetModel.ResponseVariableName = CommonGenerator.EnsureVariableNameIsNotReserved(snippetModel.ResponseVariableName, languageExpressions); //Csharp properties are uppercase so replace with list with uppercase version snippetModel.SelectFieldList = snippetModel.SelectFieldList.Select(CommonGenerator.UppercaseFirstLetter).ToList(); var actions = CommonGenerator.GenerateQuerySection(snippetModel, languageExpressions); //append any custom queries present snippetBuilder.Append(GenerateCustomQuerySection(snippetModel)); if (snippetModel.Method == HttpMethod.Get) { var extraSnippet = ""; if (segment is PropertySegment && !segment.EdmType.IsStream())//streams can be sorted out normally { extraSnippet = GeneratePropertySectionSnippet(snippetModel); snippetModel.SelectFieldList = snippetModel.SelectFieldList.Select(CommonGenerator.UppercaseFirstLetter).ToList(); actions = CommonGenerator.GenerateQuerySection(snippetModel, languageExpressions); } snippetBuilder.Append($"var {snippetModel.ResponseVariableName} = "); snippetBuilder.Append(GenerateRequestSection(snippetModel, $"{actions}\n\t.GetAsync();")); snippetBuilder.Append(extraSnippet); } else if (snippetModel.Method == HttpMethod.Post) { switch (segment) { case NavigationPropertySegment _: case EntitySetSegment _: case NavigationPropertyLinkSegment _: if (string.IsNullOrEmpty(snippetModel.RequestBody)) { throw new Exception($"No request Body present for POST of entity {snippetModel.ResponseVariableName}"); } snippetBuilder.Append($"var {snippetModel.ResponseVariableName} = "); snippetBuilder.Append(CSharpGenerateObjectFromJson(segment, snippetModel.RequestBody, new List <string> { snippetModel.ResponseVariableName })); snippetBuilder.Append(GenerateRequestSection(snippetModel, $"{actions}\n\t.AddAsync({snippetModel.ResponseVariableName});")); break; case OperationSegment _: //deserialize the object since the json top level contains the list of parameter objects if (JsonConvert.DeserializeObject(snippetModel.RequestBody) is JObject testObj) { foreach (var(key, jToken) in testObj) { var jsonString = JsonConvert.SerializeObject(jToken); snippetBuilder.Append($"var {CommonGenerator.LowerCaseFirstLetter(key)} = "); snippetBuilder.Append(CSharpGenerateObjectFromJson(segment, jsonString, new List <string> { CommonGenerator.LowerCaseFirstLetter(key) })); } } snippetBuilder.Append(GenerateRequestSection(snippetModel, $"{actions}\n\t.PostAsync();")); break; default: throw new Exception("Unknown Segment Type in URI for method POST"); } } else if (snippetModel.Method == HttpMethod.Patch) { if (string.IsNullOrEmpty(snippetModel.RequestBody)) { throw new Exception($"No request Body present for Patch of entity {snippetModel.ResponseVariableName}"); } snippetBuilder.Append($"var {snippetModel.ResponseVariableName} = "); snippetBuilder.Append(CSharpGenerateObjectFromJson(segment, snippetModel.RequestBody, new List <string> { snippetModel.ResponseVariableName })); if (segment is PropertySegment) { snippetBuilder.Append(GeneratePropertySectionSnippet(snippetModel)); } snippetBuilder.Append(GenerateRequestSection(snippetModel, $"{actions}\n\t.UpdateAsync({snippetModel.ResponseVariableName});")); } else if (snippetModel.Method == HttpMethod.Delete) { snippetBuilder.Append(GenerateRequestSection(snippetModel, $"{actions}\n\t.DeleteAsync();")); } else if (snippetModel.Method == HttpMethod.Put) { if (string.IsNullOrEmpty(snippetModel.RequestBody)) { throw new Exception($"No request Body present for PUT of entity {snippetModel.ResponseVariableName}"); } if (snippetModel.ContentType.Equals("application/json", StringComparison.OrdinalIgnoreCase)) { snippetBuilder.Append($"var {snippetModel.ResponseVariableName} = "); snippetBuilder.Append(CSharpGenerateObjectFromJson(segment, snippetModel.RequestBody, new List <string> { snippetModel.ResponseVariableName })); } else { snippetBuilder.Append($"var {snippetModel.ResponseVariableName} = \"{snippetModel.RequestBody.Trim()}\"\n\n"); } snippetBuilder.Append(GenerateRequestSection(snippetModel, $"{actions}\n\t.PutAsync({snippetModel.ResponseVariableName});")); } else { throw new NotImplementedException("HTTP method not implemented for C#"); } return(snippetBuilder.ToString()); } catch (Exception ex) { throw new Exception(ex.Message); } }
/// <summary> /// Formulates the resources part of the generated snippets /// </summary> /// <param name="snippetModel">Model of the Snippets info <see cref="SnippetModel"/></param> /// <returns>String of the resources in Csharp code</returns> private static string CSharpGenerateResourcesPath(SnippetModel snippetModel) { var resourcesPath = new StringBuilder(); var resourcesPathSuffix = string.Empty; // lets append all resources foreach (var item in snippetModel.Segments) { switch (item) { //handle indexing into collections case KeySegment keySegment: resourcesPath.Append($"[\"{keySegment.Keys.FirstOrDefault().Value}\"]"); break; //handle functions/actions and any parameters present into collections case OperationSegment operationSegment: var paramList = CommonGenerator.GetParameterListFromOperationSegment(operationSegment, snippetModel); resourcesPath.Append($"\n\t.{CommonGenerator.UppercaseFirstLetter(operationSegment.Identifier)}({CommonGenerator.GetListAsStringForSnippet(paramList, ",")})"); break; case ValueSegment _: resourcesPath.Append(".Content"); break; case PropertySegment propertySegment: //don't append anything that is not a stream since this is not accessed directly in C# if (propertySegment.EdmType.IsStream()) { resourcesPath.Append($".{CommonGenerator.UppercaseFirstLetter(item.Identifier)}"); } break; case ReferenceSegment _: resourcesPath.Append(".Reference"); break; case NavigationPropertyLinkSegment _: /* * The ODataURIParser may sometimes not create and add a ReferenceSegment object to the end of * the segments collection in the event that there is a valid NavigationPropertySegment in the * collection. It will replace this NavigationPropertySegment object with a NavigationPropertyLinkSegment * object. Therefore we modify the suffix so that it may be appended to show the Reference section since * the $ref should always be last in a valid Odata URI. */ if (snippetModel.Path.Contains("$ref") && !(snippetModel.Segments.Last() is ReferenceSegment)) { var nextSegmentIndex = snippetModel.Segments.IndexOf(item) + 1; if (nextSegmentIndex >= snippetModel.Segments.Count) { nextSegmentIndex = snippetModel.Segments.Count - 1; } var nextSegment = snippetModel.Segments[nextSegmentIndex]; //check if the next segment is a KeySegment to know if we will be accessing a single entity of a collection. resourcesPathSuffix = (item.EdmType is IEdmCollectionType) && !(nextSegment is KeySegment) ? ".References" : ".Reference"; } resourcesPath.Append($".{CommonGenerator.UppercaseFirstLetter(item.Identifier)}"); break; default: //its most likely just a resource so append it resourcesPath.Append($".{CommonGenerator.UppercaseFirstLetter(item.Identifier)}"); break; } } if (!string.IsNullOrEmpty(resourcesPathSuffix)) { resourcesPath.Append(resourcesPathSuffix); } return(resourcesPath.ToString()); }
/// <summary> /// Formulates the requested Graph snippets and returns it as string for Objective C /// </summary> /// <param name="snippetModel">Model of the Snippets info <see cref="SnippetModel"/></param> /// <param name="languageExpressions">The language expressions to be used for code Gen</param> /// <returns>String of the snippet in Objective C code</returns> public static string GenerateCodeSnippet(SnippetModel snippetModel, LanguageExpressions languageExpressions) { StringBuilder snippetBuilder = new StringBuilder(); var segment = snippetModel.Segments.Last(); /*Auth provider section*/ snippetBuilder.Append("MSHTTPClient *httpClient = [MSClientFactory createHTTPClientWithAuthenticationProvider:authenticationProvider];\r\n\r\n"); /*Request generation section*/ snippetBuilder.Append($"NSString *MSGraphBaseURL = @\"{snippetModel.ODataUriParser.ServiceRoot}\";\r\n"); snippetBuilder.Append($"NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[MSGraphBaseURL stringByAppendingString:@\"{snippetModel.Path}{snippetModel.QueryString}\"]]];\r\n"); snippetBuilder.Append($"[urlRequest setHTTPMethod:@\"{snippetModel.Method}\"];\r\n"); //add header to the Request headers list so it used in the request builder if (snippetModel.ContentType != null) { snippetModel.RequestHeaders = snippetModel.RequestHeaders.Append(new KeyValuePair <string, IEnumerable <string> >("Content-Type", new[] { snippetModel.ContentType })); } snippetBuilder.Append(CommonGenerator.GenerateQuerySection(snippetModel, languageExpressions)); snippetBuilder.Append("\r\n"); //create object to send out now that we have a payload it is not null if (!string.IsNullOrEmpty(snippetModel.RequestBody)) { if (segment is OperationSegment) { snippetBuilder.Append("NSMutableDictionary *payloadDictionary = [[NSMutableDictionary alloc] init];\r\n\r\n"); //deserialize the object since the json top level contains the list of parameter objects if (JsonConvert.DeserializeObject(snippetModel.RequestBody) is JObject testObj) { foreach (var(key, jToken) in testObj) { var jsonString = JsonConvert.SerializeObject(jToken); snippetBuilder.Append(ObjectiveCGenerateObjectFromJson(segment, jsonString, new List <string> { CommonGenerator.LowerCaseFirstLetter(key) })); snippetBuilder.Append(jToken.Type == JTokenType.Array ? $"payloadDictionary[@\"{key}\"] = {CommonGenerator.LowerCaseFirstLetter(key)}List;\r\n\r\n" : $"payloadDictionary[@\"{key}\"] = {CommonGenerator.LowerCaseFirstLetter(key)};\r\n\r\n"); } } snippetBuilder.Append("NSData *data = [NSJSONSerialization dataWithJSONObject:payloadDictionary options:kNilOptions error:&error];\r\n"); snippetBuilder.Append("[urlRequest setHTTPBody:data];\r\n\r\n"); } else { snippetBuilder.Append($"{ObjectiveCGenerateObjectFromJson(snippetModel.Segments.Last(), snippetModel.RequestBody, new List<string> {snippetModel.ResponseVariableName})}\r\n"); snippetBuilder.Append("NSError *error;\r\n"); snippetBuilder.Append($"NSData *{snippetModel.ResponseVariableName}Data = [{snippetModel.ResponseVariableName} getSerializedDataWithError:&error];\r\n"); snippetBuilder.Append($"[urlRequest setHTTPBody:{snippetModel.ResponseVariableName}Data];\r\n\r\n"); } } /*Task/Response generation section*/ snippetBuilder.Append("MSURLSessionDataTask *meDataTask = [httpClient dataTaskWithRequest:urlRequest \r\n"); snippetBuilder.Append("\tcompletionHandler: ^(NSData *data, NSURLResponse *response, NSError *nserror) {\r\n\r\n"); //typically a GET request a json payload of the entity coming back if (snippetModel.Method == HttpMethod.Get) { if (segment.EdmType is IEdmCollectionType collectionType) { //Get underlying model if it is an entity if (collectionType.ElementType.Definition is IEdmNamedElement edmNamedElement) { snippetModel.ResponseVariableName = edmNamedElement.Name; } snippetBuilder.Append("\t\tNSError *jsonError = nil;\r\n"); snippetBuilder.Append("\t\tMSCollection *collection = [[MSCollection alloc] initWithData:data error:&jsonError];\r\n"); snippetBuilder.Append($"\t\tMSGraph{CommonGenerator.UppercaseFirstLetter(snippetModel.ResponseVariableName)} *{snippetModel.ResponseVariableName} " + $"= [[MSGraph{CommonGenerator.UppercaseFirstLetter(snippetModel.ResponseVariableName)} alloc] " + "initWithDictionary:[[collection value] objectAtIndex: 0] error:&nserror];\r\n\r\n"); } else { snippetBuilder.Append($"\t\tMSGraph{CommonGenerator.UppercaseFirstLetter(snippetModel.ResponseVariableName)} *{snippetModel.ResponseVariableName} " + $"= [[MSGraph{CommonGenerator.UppercaseFirstLetter(snippetModel.ResponseVariableName)} alloc] initWithData:data error:&nserror];\r\n\r\n"); } } else { snippetBuilder.Append("\t\t//Request Completed\r\n\r\n"); } snippetBuilder.Append("}];\r\n\r\n"); snippetBuilder.Append("[meDataTask execute];"); return(snippetBuilder.ToString()); }
/// <summary> /// Formulates the requested Graph snippets and returns it as string for Java /// </summary> /// <param name="snippetModel">Model of the Snippets info <see cref="SnippetModel"/></param> /// <param name="languageExpressions">The language expressions to be used for code Gen</param> /// <returns>String of the snippet in Java code</returns> public static string GenerateCodeSnippet(SnippetModel snippetModel, LanguageExpressions languageExpressions) { /* As the Java beta SDK is not implemented yet throw this exception till it is */ if (snippetModel.ApiVersion.Equals("beta")) { throw new NotImplementedException("Java Beta SDK not implemented yet"); } var snippetBuilder = new StringBuilder(); try { var segment = snippetModel.Segments.Last(); snippetModel.ResponseVariableName = CommonGenerator.EnsureVariableNameIsNotReserved(snippetModel.ResponseVariableName, languageExpressions); /*Auth provider section*/ snippetBuilder.Append("IGraphServiceClient graphClient = GraphServiceClient.builder().authenticationProvider( authProvider ).buildClient();\r\n\r\n"); //append any request options present snippetBuilder.Append(GenerateRequestOptionsSection(snippetModel, languageExpressions)); /*Generate the query section of the request*/ var requestActions = CommonGenerator.GenerateQuerySection(snippetModel, languageExpressions); if (snippetModel.Method == HttpMethod.Get) { var typeName = GetJavaReturnTypeName(segment.EdmType, snippetModel.ResponseVariableName); snippetBuilder.Append($"{typeName} {snippetModel.ResponseVariableName} = "); if (segment is PropertySegment) { return(GenerateCustomRequestForPropertySegment(snippetBuilder, snippetModel)); } snippetBuilder.Append(GenerateRequestSection(snippetModel, $"{requestActions}\n\t.get();")); } else if (snippetModel.Method == HttpMethod.Post) { switch (segment) { case NavigationPropertySegment _: case EntitySetSegment _: case NavigationPropertyLinkSegment _: if (string.IsNullOrEmpty(snippetModel.RequestBody)) { throw new Exception("No request Body present for Java POST request"); } snippetBuilder.Append(JavaGenerateObjectFromJson(segment, snippetModel.RequestBody, new List <string> { snippetModel.ResponseVariableName })); snippetBuilder.Append(GenerateRequestSection(snippetModel, $"{requestActions}\n\t.post({snippetModel.ResponseVariableName});")); break; case OperationSegment _: //deserialize the object since the json top level contains the list of parameter objects if (JsonConvert.DeserializeObject(snippetModel.RequestBody) is JObject testObj) { foreach (var(key, jToken) in testObj) { var jsonString = JsonConvert.SerializeObject(jToken); snippetBuilder.Append(JavaGenerateObjectFromJson(segment, jsonString, new List <string> { CommonGenerator.LowerCaseFirstLetter(key) })); } } snippetBuilder.Append(GenerateRequestSection(snippetModel, $"{requestActions}\n\t.post();")); break; default: throw new Exception("Unknown Segment Type in URI for method POST"); } } else if (snippetModel.Method == HttpMethod.Patch) { if (string.IsNullOrEmpty(snippetModel.RequestBody)) { throw new Exception("No request Body present for Java Patch request"); } snippetBuilder.Append(JavaGenerateObjectFromJson(segment, snippetModel.RequestBody, new List <string> { snippetModel.ResponseVariableName })); if (segment is PropertySegment) { return(GenerateCustomRequestForPropertySegment(snippetBuilder, snippetModel)); } snippetBuilder.Append(GenerateRequestSection(snippetModel, $"{requestActions}\n\t.patch({snippetModel.ResponseVariableName});")); } else if (snippetModel.Method == HttpMethod.Put) { if (string.IsNullOrEmpty(snippetModel.RequestBody)) { throw new Exception("No request Body present for Java Put request"); } snippetBuilder.Append(JavaGenerateObjectFromJson(segment, snippetModel.RequestBody, new List <string> { snippetModel.ResponseVariableName })); snippetBuilder.Append(GenerateRequestSection(snippetModel, $"{requestActions}\n\t.put({snippetModel.ResponseVariableName});")); } else if (snippetModel.Method == HttpMethod.Delete) { snippetBuilder.Append(GenerateRequestSection(snippetModel, $"{requestActions}\n\t.delete();")); } else { throw new NotImplementedException("HTTP method not implemented for Java"); } return(snippetBuilder.ToString()); } catch (Exception ex) { throw new Exception(ex.Message); } }
/// <summary> /// Formulates the requested Graph snippets and returns it as string for JavaScript /// </summary> /// <param name="snippetModel">Model of the Snippets info <see cref="SnippetModel"/></param> /// <param name="languageExpressions">The language expressions to be used for code Gen</param> /// <returns>String of the snippet in Javascript code</returns> public static string GenerateCodeSnippet(SnippetModel snippetModel, LanguageExpressions languageExpressions) { try { var snippetBuilder = new StringBuilder(); snippetModel.ResponseVariableName = CommonGenerator.EnsureVariableNameIsNotReserved(snippetModel.ResponseVariableName, languageExpressions); //setup the auth snippet section snippetBuilder.Append("const options = {\n"); snippetBuilder.Append("\tauthProvider,\n};\n\n"); //init the client snippetBuilder.Append("const client = Client.init(options);\n\n"); if (snippetModel.Method == HttpMethod.Get) { //append any queries with the actions var getActions = CommonGenerator.GenerateQuerySection(snippetModel, languageExpressions) + "\n\t.get();"; snippetBuilder.Append(GenerateRequestSection(snippetModel, getActions)); } else if (snippetModel.Method == HttpMethod.Post) { if (!string.IsNullOrEmpty(snippetModel.RequestBody)) { snippetBuilder.Append(JavascriptGenerateObjectFromJson(snippetModel.RequestBody, snippetModel.ResponseVariableName)); snippetBuilder.Append(GenerateRequestSection(snippetModel, $"\n\t.post({snippetModel.ResponseVariableName});")); } else { snippetBuilder.Append(GenerateRequestSection(snippetModel, "\n\t.post();")); } } else if (snippetModel.Method == HttpMethod.Patch) { if (string.IsNullOrEmpty(snippetModel.RequestBody)) { throw new Exception("No body present for PATCH method in Javascript"); } snippetBuilder.Append(JavascriptGenerateObjectFromJson(snippetModel.RequestBody, snippetModel.ResponseVariableName)); snippetBuilder.Append(GenerateRequestSection(snippetModel, $"\n\t.update({snippetModel.ResponseVariableName});")); } else if (snippetModel.Method == HttpMethod.Delete) { snippetBuilder.Append(GenerateRequestSection(snippetModel, "\n\t.delete();")); } else if (snippetModel.Method == HttpMethod.Put) { if (string.IsNullOrEmpty(snippetModel.RequestBody)) { throw new Exception("No body present for PUT method in Javascript"); } snippetBuilder.Append(JavascriptGenerateObjectFromJson(snippetModel.RequestBody, snippetModel.ResponseVariableName)); snippetBuilder.Append(GenerateRequestSection(snippetModel, $"\n\t.put({snippetModel.ResponseVariableName});")); } else { throw new NotImplementedException("HTTP method not implemented for Javascript"); } return(snippetBuilder.ToString()); } catch (Exception ex) { throw new Exception(ex.Message); } }
/// <summary> /// Generate a list of Request options and populate them as needed in a java specific function. /// Java does not have helper functions for all the odata parameter types and therefore the missing ones are inserted as query options here. /// </summary> /// <param name="snippetModel">Snippet model built from the request</param> /// <param name="languageExpressions">Language Expressions needed for the language</param> private static string GenerateRequestOptionsSection(SnippetModel snippetModel, LanguageExpressions languageExpressions) { if (!JavaModelHasRequestOptionsParameters(snippetModel)) { return(string.Empty); //nothing to do here } var stringBuilder = new StringBuilder(); var requestOptionsPattern = "requestOptions.add(new QueryOption(\"{0}\", \"{1}\"));\r\n"; stringBuilder.Append("LinkedList<Option> requestOptions = new LinkedList<Option>();\r\n"); //insert any header options options foreach (var(key, value) in snippetModel.RequestHeaders) { if (key.ToLower().Equals("host", StringComparison.Ordinal))//no need to generate source for the host header { continue; } //append the header to the snippet var valueString = value.First().Replace("\"", languageExpressions.DoubleQuotesEscapeSequence); stringBuilder.Append($"requestOptions.add(new HeaderOption(\"{key}\", \"{valueString}\"));\r\n"); } //insert any custom query options foreach (var(key, value) in snippetModel.CustomQueryOptions) { stringBuilder.Append(string.Format(requestOptionsPattern, key, value)); } //Append any filter queries if (snippetModel.FilterFieldList.Any()) { var filterResult = CommonGenerator.GetListAsStringForSnippet(snippetModel.FilterFieldList, languageExpressions.FilterExpressionDelimiter); stringBuilder.Append(string.Format(requestOptionsPattern, "$filter", filterResult));//append the filter to the snippet } //Append any order by queries if (snippetModel.OrderByFieldList.Any()) { var orderByResult = CommonGenerator.GetListAsStringForSnippet(snippetModel.OrderByFieldList, languageExpressions.OrderByExpressionDelimiter); stringBuilder.Append(string.Format(requestOptionsPattern, "$orderby", orderByResult));//append the order by result to the snippet } //Append any skip queries if (snippetModel.ODataUri.Skip.HasValue) { stringBuilder.Append(string.Format(requestOptionsPattern, "$skip", snippetModel.ODataUri.Skip)); } //Append any search queries if (!string.IsNullOrEmpty(snippetModel.SearchExpression)) { stringBuilder.Append(string.Format(requestOptionsPattern, "$search", snippetModel.SearchExpression)); } //Append any skip token queries if (!string.IsNullOrEmpty(snippetModel.ODataUri.SkipToken)) { stringBuilder.Append(string.Format(requestOptionsPattern, "$skiptoken", snippetModel.ODataUri.SkipToken)); } //return request options section with a new line appended return($"{stringBuilder}\r\n"); }
/// <summary> /// ObjectiveCGenerator constructor /// </summary> /// <param name="model">Model representing metadata</param> public ObjectiveCGenerator(IEdmModel model) { CommonGenerator = new CommonGenerator(model); }
/// <summary> /// Objective C function to generate Object constructor section of a code snippet. In the event that another object is needed in the middle of generation, /// a recursive call is made to sort out the needed object. /// </summary> /// <param name="pathSegment">Odata Function/Entity from which the object is needed</param> /// <param name="jsonBody">Json string from which the information of the object to be initialized is held</param> /// <param name="path">List of strings/identifier showing the path through the Edm/json structure to reach the Class Identifier from the segment</param> private static string ObjectiveCGenerateObjectFromJson(ODataPathSegment pathSegment, string jsonBody, ICollection <string> path) { var stringBuilder = new StringBuilder(); var jsonObject = JsonConvert.DeserializeObject(jsonBody); switch (jsonObject) { case JObject jObject: { var className = GetObjectiveCModelName(pathSegment, path); stringBuilder.Append($"{className} *{path.Last()} = [[{className} alloc] init];\r\n"); //initialize each member/property of the object foreach (var(key, jToken) in jObject) { var value = JsonConvert.SerializeObject(jToken); var newPath = path.Append(key).ToList(); //add new identifier to the path if (key.Contains("@odata")) { continue; } switch (jToken.Type) { case JTokenType.Array: case JTokenType.Object: //new nested object needs to be constructed so call this function recursively to make it var newObject = ObjectiveCGenerateObjectFromJson(pathSegment, value, newPath).TrimEnd(); stringBuilder.Append($"{newObject}\r\n"); stringBuilder.Append(jToken.Type == JTokenType.Array ? $"[{path.Last()} set{CommonGenerator.UppercaseFirstLetter(key)}:{newPath.Last()}List];\r\n" : $"[{path.Last()} set{CommonGenerator.UppercaseFirstLetter(key)}:{newPath.Last()}];\r\n"); break; case JTokenType.String: var enumString = GenerateEnumString(jToken.ToString(), pathSegment, newPath); //check if the type is an enum and handle it stringBuilder.Append(!string.IsNullOrEmpty(enumString) ? $"[{path.Last()} set{CommonGenerator.UppercaseFirstLetter(key)}: [{enumString}]];\r\n" : $"[{path.Last()} set{CommonGenerator.UppercaseFirstLetter(key)}:@{value.Replace("\n", "").Replace("\r", "")}];\r\n"); break; default: stringBuilder.Append($"[{path.Last()} set{CommonGenerator.UppercaseFirstLetter(key)}: {value.Replace("\n", "").Replace("\r", "")}];\r\n"); break; } } } break; case JArray array: { var objectList = array.Children <JObject>(); stringBuilder.Append($"NSMutableArray *{path.Last()}List = [[NSMutableArray alloc] init];\r\n"); if (objectList.Any()) { foreach (var item in objectList) { var jsonString = JsonConvert.SerializeObject(item); var objectStringFromJson = ObjectiveCGenerateObjectFromJson(pathSegment, jsonString, path).TrimEnd(); stringBuilder.Append($"{objectStringFromJson}\r\n"); stringBuilder.Append($"[{path.Last()}List addObject: {path.Last()}];\r\n"); } } else { //append list of strings foreach (var element in array) { stringBuilder.Append($"[{path.Last()}List addObject: @\"{element.Value<string>()}\"];\r\n"); } } } break; case string jsonString: { var enumString = GenerateEnumString(jsonString, pathSegment, path); if (!string.IsNullOrEmpty(enumString)) { var className = GetObjectiveCModelName(pathSegment, path); stringBuilder.Append($"{className} *{path.Last()} = [{enumString}];\r\n"); } else if (jsonString.Equals("true", StringComparison.OrdinalIgnoreCase)) { stringBuilder.Append($"BOOL {path.Last()} = YES;\r\n"); } else if (jsonString.Equals("false", StringComparison.OrdinalIgnoreCase)) { stringBuilder.Append($"BOOL {path.Last()} = NO;\r\n"); } else { stringBuilder.Append($"NSString *{path.Last()} = @\"{jsonString}\";\r\n"); } } break; case DateTime dateTime: { stringBuilder.Append($"NSString *{path.Last()}DateTimeString = @\"{dateTime.ToString(CultureInfo.InvariantCulture)}\";\r\n"); stringBuilder.Append($"NSDate *{path.Last()} = [NSDate ms_dateFromString: {path.Last()}DateTimeString];\r\n"); } break; case null: //do nothing break; default: var primitive = jsonObject.ToString(); //json deserializer capitalizes the bool types so undo that if (primitive.Equals("True", StringComparison.OrdinalIgnoreCase)) { stringBuilder.Append($"BOOL {path.Last()} = YES;\r\n"); } else if (primitive.Equals("False", StringComparison.OrdinalIgnoreCase)) { stringBuilder.Append($"BOOL {path.Last()} = NO;\r\n"); } else { stringBuilder.Append($"int32_t {path.Last()} = {primitive};\r\n"); } break; } return(stringBuilder.ToString()); }
/// <summary> /// JavaGenerator constructor /// </summary> /// <param name="model">Model representing metadata</param> public JavaGenerator(IEdmModel model) { CommonGenerator = new CommonGenerator(model); }
/// <summary> /// Java specific function to generate Object constructor section of a code snippet. In the event that another object is needed in the middle of generation, /// a recursive call is made to sort out the needed object. /// </summary> /// <param name="pathSegment">Odata Function/Entity from which the object is needed</param> /// <param name="jsonString">Json string from which the information of the object to be initialized is held</param> /// <param name="path">List of strings/identifier showing the path through the Edm/json structure to reach the Class Identifier from the segment</param> /// <param name="usedVarNames">List to keep track of variable names used to prevent use of the same variable name</param> private static string JavaGenerateObjectFromJson(ODataPathSegment pathSegment, string jsonString, List <string> path, List <string> usedVarNames = null) { var stringBuilder = new StringBuilder(); var jsonObject = JsonConvert.DeserializeObject(jsonString); usedVarNames = usedVarNames ?? new List <string>();//make sure list is not null switch (jsonObject) { case string _: { var enumString = GenerateEnumString(jsonObject.ToString(), pathSegment, path); if (!string.IsNullOrEmpty(enumString)) { //Enum is accessed as the Classname then enum type e.g Importance.LOW stringBuilder.Append($"{enumString.Split(".").First()} {path.Last()} = {enumString};\r\n"); } else if (jsonObject.Equals("true") || jsonObject.Equals("false")) { stringBuilder.Append($"boolean {path.Last()} = {jsonObject};\r\n"); //boolean primitives values masquerading as strings. } else { stringBuilder.Append($"String {path.Last()} = \"{jsonObject}\";\r\n"); } } break; case JObject jObject: { var currentVarName = EnsureJavaVariableNameIsUnique(path.Last(), usedVarNames); var className = GetJavaClassNameFromOdataPath(pathSegment, path); stringBuilder.Append($"{className} { currentVarName } = new {className}();\r\n"); //initialize each member/property of the object foreach (var(key, jToken) in jObject) { var value = JsonConvert.SerializeObject(jToken); var newPath = path.Append(key).ToList(); //add new identifier to the path if (key.Contains("@odata") || key.StartsWith("@")) //sometimes @odata maybe in the middle e.g."*****@*****.**" { stringBuilder = GenerateJavaAdditionalDataSection(stringBuilder, key, jToken.ToString(), className, currentVarName); continue; } switch (jToken.Type) { case JTokenType.Array: case JTokenType.Object: //new nested object needs to be constructed so call this function recursively to make it stringBuilder.Append($"{JavaGenerateObjectFromJson(pathSegment, value, newPath, usedVarNames)}"); stringBuilder.Append(jToken.Type == JTokenType.Array ? $"{ currentVarName }.{ newPath.Last() } = { EnsureJavaVariableNameIsUnique(newPath.Last()+"List", usedVarNames) };\r\n" : $"{ currentVarName }.{ newPath.Last() } = { EnsureJavaVariableNameIsUnique(newPath.Last(), usedVarNames) };\r\n"); break; case JTokenType.String: var enumString = GenerateEnumString(jToken.ToString(), pathSegment, newPath); //check if the type is an enum and handle it stringBuilder.Append(!string.IsNullOrEmpty(enumString) ? $"{ currentVarName }.{newPath.Last()} = {enumString};\r\n" : $"{ currentVarName }.{newPath.Last()} = {value.Replace("\n", "").Replace("\r", "")};\r\n"); break; default: stringBuilder.Append($"{ currentVarName }.{newPath.Last()} = { value.Replace("\n", "").Replace("\r", "") };\r\n"); break; } usedVarNames.Add(jToken.Type == JTokenType.Array? $"{newPath.Last()}List": newPath.Last()); //add used variable name to used list } } break; case JArray array: { var objectList = array.Children <JObject>(); if (objectList.Any()) { var className = GetJavaClassNameFromOdataPath(pathSegment, path); var currentListName = EnsureJavaVariableNameIsUnique(path.Last() + "List", usedVarNames); //Item is a list/array so declare a typed list stringBuilder.Append($"LinkedList<{className}> {currentListName} = new LinkedList<{className}>();\r\n"); foreach (var item in objectList) { var currentListItemName = EnsureJavaVariableNameIsUnique(path.Last(), usedVarNames); var jsonItemString = JsonConvert.SerializeObject(item); //we need to create a new object var objectStringFromJson = JavaGenerateObjectFromJson(pathSegment, jsonItemString, path, usedVarNames); stringBuilder.Append($"{objectStringFromJson}"); stringBuilder.Append($"{currentListName}.add({currentListItemName});\r\n"); usedVarNames.Add(path.Last()); //add used variable name to used list } } else { stringBuilder.Append($"LinkedList<String> {path.Last()}List = new LinkedList<String>();\r\n"); //its not nested objects but a string collection foreach (var element in array) { stringBuilder.Append($"{path.Last()}List.add(\"{element.Value<string>()}\");\r\n"); } } } break; case null: //do nothing break; default: var primitive = jsonObject.ToString(); //json deserializer capitalizes the bool types so undo that if (primitive.Equals("True", StringComparison.Ordinal) || primitive.Equals("False", StringComparison.Ordinal)) { stringBuilder.Append($"boolean {path.Last()} = {CommonGenerator.LowerCaseFirstLetter(primitive)};\r\n"); } else { stringBuilder.Append($"int {path.Last()} = {primitive};\r\n"); //item is a primitive print as is } break; } //check if this is the outermost object in a potential nested object structure and needs the semicolon termination character. return(path.Count == 1 ? $"{stringBuilder.ToString().TrimEnd()}\r\n\r\n" : stringBuilder.ToString()); }