/// <summary> /// Generates the project code module for this /// </summary> public ProjectCode GenerateCode() { var code = new ProjectCode(Name); //Rename any class called "_globals" with an extra underscore. if (Namespaces["@"].FindIndex(x => x.Name == "_globals") != -1) { Namespaces["@"].First(x => x.Name == "_globals").Name += "_"; } //Add the root address as a class called "_globals". var globalModule = new ClassModule("_globals"); globalModule.StaticFields.Add(new Static("RootURL", "string") { Value = "@\"" + RootURL + "\"" }); Namespaces["@"].Add(globalModule); //Loop over namespaces for generation. foreach (var ns in Namespaces) { //Correct any collisions in this namespace. foreach (var module in ns.Value) { while (ns.Value.FindAll(x => x.Name == module.Name).Count > 1) { module.Name += "_"; } } //Generate code for the namespace. string thisNamespaceFull = (ns.Key == "@") ? Name : Name + "." + ns.Key; foreach (var module in ns.Value) { code.Files.Add(new ProjectFileInfo() { ClassName = module.Name, Namespace = ns.Key }, module.GenerateCode(thisNamespaceFull, Name + ".Data")); } } return(code); }
/// <summary> /// Derives a C# class module from a JObject. /// </summary> public static ClassModule FromJObject(JObject jobj, string name) { //Create ClassModule. var module = new ClassModule(name); //If the name or JObject is null, invalid. if (jobj == null) { Logger.Write("[ERR] - Invalid JSON data given to create a class module from, no data. ('" + name + "')", 1); return(null); } if (name == null) { Logger.Write("[ERR] - No name provided for creating returned data from a route. ('" + name + "')", 1); return(null); } //Is the name a reserved word? while (module.Name.IsReservedWord()) { //Stick underscores on it. module.Name += "_"; } //Create the class module. foreach (var prop in jobj.Properties()) { var field = GenerateField(name, prop.Name, prop.Value); if (field.Name.ToUpper() == module.Name.ToUpper()) { //Naming conflict between module and field, rename module with extra "_". module.Name += "_"; } module.Fields.Add(field); } return(module); }
/// <summary> /// Run the currently set up instance of Burrito. /// </summary> public static int Run() { //Try and deserialize the schema. APISchema schema; try { schema = JsonConvert.DeserializeObject <APISchema>(File.ReadAllText(APISchemaPath)); } catch (Exception e) { Logger.Write("[ERR] - Failed to load API schema, '" + e.Message + "'.", 1); return(0); } //All relevant properties exist? if (schema.Sections == null || schema.Name == null || schema.RootPath == null) { Logger.Write("[ERR] - Required chema property missing (one of 'name', 'root' or 'sections').", 1); return(0); } //Valid schema name? if (!Regex.IsMatch(schema.Name, "^[A-Za-z0-9_]+$")) { Logger.Write("[ERR] - Invalid schema name given. Must only be alphanumeric or underscores.", 1); return(0); } //If the root path doesn't end with a "/", add it on. if (!schema.RootPath.EndsWith("/")) { schema.RootPath += "/"; } //Create a project module with the given schema and namespaces. Project = new ProjectModule(schema.Name, schema.RootPath); Project.AddNamespace("Data"); Project.AddNamespace("@"); Project.Namespaces["Data"].Add(new ClassModule("_empty")); //Check all the sections in the schema have unique names. var sectionNames = new List <string>(); foreach (var module in schema.Sections) { if (sectionNames.Contains(module.Name)) { Logger.Write("[ERR] - Duplicate section name '" + module.Name + "' detected.", 1); return(0); } sectionNames.Add(module.Name); } //Loop through the routes and generate code modules for each of them. foreach (var module in schema.Sections) { var moduleClass = new ClassModule(module.Name); foreach (var route in module.Routes) { //Check whether the route has valid properties. if (!route.Validate()) { return(0); } //What method does this route use? switch (route.HTTPMethod) { case null: Logger.Write("[ERR] - No HTTP method defined for route '" + route.RelativeURL + "'.", 1); return(0); case "GET": case "get": //Figure out a data type from the API endpoint (if possible). bool isList = false; var classReturned = DataTypeCreator.DeriveFromRoute(schema.RootPath, route, ref isList); if (classReturned == null) { break; } //Add class. Project.Namespaces["Data"].Add(classReturned); //Add the method. moduleClass.Methods.Add(new GETMethodModule() { Async = route.Async, Route = route.RelativeURL, RouteParams = route.GetRouteVariables(), XMLSummary = route.GetSummary(), Name = route.GetMethodName(), ReceivedDataType = classReturned, ReturnsList = isList }); break; case "POST": case "post": //Figure out a data type it should receive. bool returnsList = false; var postClassReturned = DataTypeCreator.DeriveFromRoute(schema.RootPath, route, ref returnsList, route.ExampleData); if (postClassReturned == null) { break; } //Figure out the data type is should send. if (route.ExampleData == null) { Logger.Write("[ERR] - Cannot implement POST route '" + route.RelativeURL + "' without example data to send.", 1); break; } if (route.SentDataName == null) { Logger.Write("[ERR] - Sent data name not set for POST route '" + route.RelativeURL + "', skipping.", 1); break; } var postClass = DataTypeCreator.FromJObject(route.ExampleData, route.SentDataName); if (postClass == null) { break; } //Add classes. Project.Namespaces["Data"].Add(postClassReturned); Project.Namespaces["Data"].Add(postClass); //Add the method. moduleClass.Methods.Add(new POSTMethodModule() { Async = route.Async, Route = route.RelativeURL, XMLSummary = route.GetSummary(), RouteParams = route.GetRouteVariables(), SentDataType = postClass, ReceivedDataType = postClassReturned, Name = route.GetMethodName(), ReturnsList = returnsList }); break; } } //Add the class to root namespace. Project.Namespaces["@"].Add(moduleClass); } //All classes generated, generate code for the project. ProjectCode code = Project.GenerateCode(); //Decide what to do with it. if (CompileMode) { code.CompileToDLL(GenerationPath); } else { try { code.CompileToProject(GenerationPath); } catch (Exception e) { Logger.Write("[ERR] - Error writing project to disk: '" + e.Message + "'.", 1); } } //If a NuSpec needs to be generated, do it now. if (GenerateNuspec) { code.GenerateNuspec(GenerationPath); } return(code.Files.Count); }
/// <summary> /// Generates a field to use in a generated class. /// </summary> private static Field GenerateField(string rootName, string name, JToken value) { switch (value.Type) { //array case JTokenType.Array: //How long is the array? var arr = (JArray)value; if (arr.Count == 0) { Logger.Write("[WARN] - Cannot generate a type from an empty array. Leaving an empty list type here. ('" + name + "')", 2); return(new Field(name, ClassModule.Empty())); } //generate field var subObjField = GenerateField(rootName, name, arr[0]); subObjField.IsList = true; //return field return(subObjField); //object (new class) case JTokenType.Object: //generate type. var subObjType = FromJObject((JObject)value, rootName + "_" + name); while (BurritoAPI.Project.Namespaces["Data"].FindIndex(x => x.Name == subObjType.Name) != -1) { subObjType.Name += "_"; } BurritoAPI.Project.Namespaces["Data"].Add(subObjType); //add field. return(new Field(name, subObjType.Name)); //raw values case JTokenType.Boolean: return(new Field(name, "bool")); case JTokenType.Bytes: return(new Field(name, "byte[]")); case JTokenType.Date: return(new Field(name, "DateTime")); case JTokenType.Float: return(new Field(name, "float")); case JTokenType.Guid: return(new Field(name, "Guid")); case JTokenType.Integer: return(new Field(name, "int")); case JTokenType.String: return(new Field(name, "string")); case JTokenType.Null: Logger.Write("[WARN] - Null type in JSON at property '" + name + "', assumed as an object. ('" + name + "')", 2); return(new Field(name, "object ")); default: Logger.Write("[WARN] - Unsupported type in JSON to create a class from: '" + value.Type.ToString() + "' for generated data class '" + name + "'. Skipped property.", 2); return(null); } }