diff --git a/conf/scala/sample/build.xml b/conf/scala/sample/build.xml index b482a4eddb7..07eaa6d3aad 100644 --- a/conf/scala/sample/build.xml +++ b/conf/scala/sample/build.xml @@ -36,6 +36,12 @@ + + + + + + @@ -62,15 +68,6 @@ - - - - - - - - - @@ -94,7 +91,6 @@ - @@ -115,14 +111,7 @@ - - - - - - - diff --git a/conf/scala/templates/ResourceObject.st b/conf/scala/templates/ResourceObject.st index 2b6a5f358ee..1088ab48bad 100644 --- a/conf/scala/templates/ResourceObject.st +++ b/conf/scala/templates/ResourceObject.st @@ -93,15 +93,15 @@ $endif$ //make the API Call $if(method.hasResponseValue)$ $if(method.postObject)$ - val response = APIInvoker.invokeAPI(resourcePath, method, queryParams, postData) + val response = APIInvoker.getApiInvoker.invokeAPI(resourcePath, method, queryParams, postData) $else$ - val response = APIInvoker.invokeAPI(resourcePath, method, queryParams, null) + val response = APIInvoker.getApiInvoker.invokeAPI(resourcePath, method, queryParams, null) $endif$ $else$ $if(method.postObject)$ - APIInvoker.invokeAPI(resourcePath, method, queryParams, postData) + APIInvoker.getApiInvoker.invokeAPI(resourcePath, method, queryParams, postData) $else$ - APIInvoker.invokeAPI(resourcePath, method, queryParams, null) + APIInvoker.getApiInvoker.invokeAPI(resourcePath, method, queryParams, null) $endif$ $endif$ $if(!method.responseVoid)$ diff --git a/src/main/java/com/wordnik/swagger/codegen/FieldDefinition.java b/src/main/java/com/wordnik/swagger/codegen/FieldDefinition.java index 7a4fb6afeb1..f12b1a6935d 100644 --- a/src/main/java/com/wordnik/swagger/codegen/FieldDefinition.java +++ b/src/main/java/com/wordnik/swagger/codegen/FieldDefinition.java @@ -20,17 +20,11 @@ import java.util.List; public class FieldDefinition { - private String returnType; - private String name; - private String initialization; - private List importDefinitions = new ArrayList(); - private String collectionItemType; - private String collectionItemName; private boolean hasListResponse; @@ -80,7 +74,7 @@ public void setReturnType(String returnType) { public String getName() { return name; } - + public void setName(String name) { this.name = name; } diff --git a/src/main/java/com/wordnik/swagger/codegen/LibraryCodeGenerator.java b/src/main/java/com/wordnik/swagger/codegen/LibraryCodeGenerator.java index 1f2cbb2a826..dd0fc64ad0b 100644 --- a/src/main/java/com/wordnik/swagger/codegen/LibraryCodeGenerator.java +++ b/src/main/java/com/wordnik/swagger/codegen/LibraryCodeGenerator.java @@ -17,10 +17,10 @@ import com.wordnik.swagger.codegen.api.SwaggerResourceDocReader; import com.wordnik.swagger.codegen.config.*; -import com.wordnik.swagger.codegen.config.ApiConfiguration; import com.wordnik.swagger.codegen.config.common.CamelCaseNamingPolicyProvider; import com.wordnik.swagger.codegen.exception.CodeGenerationException; import com.wordnik.swagger.codegen.resource.*; + import org.antlr.stringtemplate.StringTemplate; import org.antlr.stringtemplate.StringTemplateGroup; import org.codehaus.jackson.map.DeserializationConfig; @@ -39,7 +39,6 @@ * Time: 6:59 PM */ public class LibraryCodeGenerator { - private static String VERSION_OBJECT_TEMPLATE = "VersionChecker"; private static String MODEL_OBJECT_TEMPLATE = "ModelObject"; private static String API_OBJECT_TEMPLATE = "ResourceObject"; @@ -49,6 +48,7 @@ public class LibraryCodeGenerator { protected static final String PACKAGE_NAME = "packageName"; protected ApiConfiguration config = null; protected LanguageConfiguration languageConfig = null; + protected ReservedWordMapper reservedWordMapper = new DefaultReservedWordMapper(); private SwaggerResourceDocReader apiMarshaller; protected DataTypeMappingProvider dataTypeMappingProvider; @@ -64,7 +64,6 @@ public LibraryCodeGenerator(String configPath){ protected void initializeWithConfigPath(String configPath){ final ObjectMapper mapper = new ObjectMapper(); mapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false); - final File configFile = new File(configPath); this.setApiConfig(readApiConfiguration(configPath, mapper, configFile)); this.setCodeGenRulesProvider(readRulesProviderConfig(configPath, mapper, configFile)); @@ -102,6 +101,7 @@ public void generateCode() { apiMarshaller = new SwaggerResourceDocReader(this.config, this.getDataTypeMappingProvider(), this.getNameGenerator()); //read resources and get their documentation List resources = apiMarshaller.readResourceDocumentation(); + preprocess(resources); StringTemplateGroup aTemplateGroup = new StringTemplateGroup("templates", languageConfig.getTemplateLocation()); if(resources.size() > 0) { generateVersionHelper(resources.get(0).getApiVersion(), aTemplateGroup); @@ -120,6 +120,21 @@ public void generateCode() { } /** + * prepares the model for template generation + * @param resources + */ + protected void preprocess(List resources) { + for(Resource resource: resources) { + for(Model model : resource.getModels()){ + // apply keyword mapping + for(ModelField modelField : model.getFields()){ + modelField.setName(reservedWordMapper.translate(modelField.getName())); + } + } + } + } + + /** * Generates version file based on the version number received from the doc calls. This version file is used * while making the API calls to make sure Client and back end are compatible. * @param version @@ -170,7 +185,7 @@ private void generateModelClasses(List resources, StringTemplateGroup } /** - * Generates assembler classes if the API returns more than one objects. + * Generates assembler classes if the API returns more than one object. * @param resources * @param templateGroup */ @@ -189,6 +204,7 @@ private void generateModelClassesForInput(List resources, StringTempla List imports = new ArrayList(); imports.addAll(this.config.getDefaultModelImports()); for(ModelField param : model.getFields()){ + param.setName(reservedWordMapper.translate(param.getName())); for(String importDef : param.getFieldDefinition(this.getDataTypeMappingProvider(), config, nameGenerator).getImportDefinitions()){ if(!imports.contains(importDef)){ imports.add(importDef); @@ -340,23 +356,26 @@ private void generateAPIClasses(List resources, StringTemplateGroup te methods = resource.generateMethods(resource, dataTypeMappingProvider, nameGenerator); StringTemplate template = templateGroup.getInstanceOf(API_OBJECT_TEMPLATE); String className = resource.generateClassName(nameGenerator); - List filteredMethods = new ArrayList(); - for(ResourceMethod method:methods){ - if(!this.getCodeGenRulesProvider().isMethodIgnored(className, method.getName())){ - filteredMethods.add(method); - } + + if(className != null){ + List filteredMethods = new ArrayList(); + for(ResourceMethod method:methods){ + if(!this.getCodeGenRulesProvider().isMethodIgnored(className, method.getName())){ + filteredMethods.add(method); + } + } + template.setAttribute("imports", imports); + template.setAttribute(PACKAGE_NAME, config.getApiPackageName()); + template.setAttribute("annotationPackageName", languageConfig.getAnnotationPackageName()); + template.setAttribute("modelPackageName", config.getModelPackageName()); + template.setAttribute("exceptionPackageName", languageConfig.getExceptionPackageName()); + template.setAttribute("resource", className); + template.setAttribute("methods", filteredMethods); + template.setAttribute("extends", config.getServiceBaseClass(className)); + + File aFile = new File(languageConfig.getResourceClassLocation()+ resource.generateClassName(nameGenerator) +languageConfig.getClassFileExtension()); + writeFile(aFile, template.toString(), "API Classes"); } - template.setAttribute("imports", imports); - template.setAttribute(PACKAGE_NAME, config.getApiPackageName()); - template.setAttribute("annotationPackageName", languageConfig.getAnnotationPackageName()); - template.setAttribute("modelPackageName", config.getModelPackageName()); - template.setAttribute("exceptionPackageName", languageConfig.getExceptionPackageName()); - template.setAttribute("resource", className); - template.setAttribute("methods", filteredMethods); - template.setAttribute("extends", config.getServiceBaseClass(className)); - - File aFile = new File(languageConfig.getResourceClassLocation()+ resource.generateClassName(nameGenerator) +languageConfig.getClassFileExtension()); - writeFile(aFile, template.toString(), "API Classes"); }catch(RuntimeException t){ System.out.println("Failed generating api class for the resource : " + resource.getResourcePath()); throw t; diff --git a/src/main/java/com/wordnik/swagger/codegen/ResourceMethod.java b/src/main/java/com/wordnik/swagger/codegen/ResourceMethod.java index 7cc494bebb7..c56782a37db 100644 --- a/src/main/java/com/wordnik/swagger/codegen/ResourceMethod.java +++ b/src/main/java/com/wordnik/swagger/codegen/ResourceMethod.java @@ -21,44 +21,25 @@ import java.util.List; public class ResourceMethod { - private String title; - private String description; - private List arguments; - private List queryParameters; - private List pathParameters; - - //set the original response name, this is used in identifying if the respone is single valued or multi valued + //set the original response name, this is used in identifying if the response is single valued or multi valued private String returnValueFromOperationJson; - private String returnValue; - private String returnClassName; - private String exceptionDescription; - private List argumentDefinitions; - private List argumentNames; - private String name; - private boolean authToken; - private String resourcePath; - private String methodType; - private boolean postObject; - private Model inputModel; - private Model listWrapperModel; - private boolean hasResponseValue; public boolean isHasResponseValue(){ diff --git a/src/main/java/com/wordnik/swagger/codegen/config/DefaultReservedWordMapper.java b/src/main/java/com/wordnik/swagger/codegen/config/DefaultReservedWordMapper.java new file mode 100644 index 00000000000..f092f1d9b12 --- /dev/null +++ b/src/main/java/com/wordnik/swagger/codegen/config/DefaultReservedWordMapper.java @@ -0,0 +1,31 @@ +/** + * Copyright 2011 Wordnik, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.wordnik.swagger.codegen.config; + +/** + * class to translate reserved words to safe variable names + * + * @author tony + * + */ +public class DefaultReservedWordMapper implements ReservedWordMapper { + @Override + public String translate(String input) { + // does nothing + return input; + } +} diff --git a/src/main/java/com/wordnik/swagger/codegen/config/LanguageConfiguration.java b/src/main/java/com/wordnik/swagger/codegen/config/LanguageConfiguration.java index 3228eb3bb32..1577e7bdc9e 100644 --- a/src/main/java/com/wordnik/swagger/codegen/config/LanguageConfiguration.java +++ b/src/main/java/com/wordnik/swagger/codegen/config/LanguageConfiguration.java @@ -24,25 +24,15 @@ * Time: 8:01 AM */ public class LanguageConfiguration { - private String classFileExtension; - private String templateLocation; - private String structureLocation; - private String libraryHome; - private String modelClassLocation; - private String resourceClassLocation; - private String exceptionPackageName; - private String annotationPackageName; - private boolean isModelEnumRequired = true; - private boolean isOutputWrapperRequired = false; public String getClassFileExtension() { @@ -129,4 +119,17 @@ public void setOutputWrapperRequired(boolean outputWrapperRequired) { public boolean isOutputWrapperRequired() { return isOutputWrapperRequired; } + + @Override + public String toString() { + return "LanguageConfiguration [classFileExtension=" + + classFileExtension + ", templateLocation=" + templateLocation + + ", structureLocation=" + structureLocation + ", libraryHome=" + + libraryHome + ", modelClassLocation=" + modelClassLocation + + ", resourceClassLocation=" + resourceClassLocation + + ", exceptionPackageName=" + exceptionPackageName + + ", annotationPackageName=" + annotationPackageName + + ", isModelEnumRequired=" + isModelEnumRequired + + ", isOutputWrapperRequired=" + isOutputWrapperRequired + "]"; + } } diff --git a/src/main/java/com/wordnik/swagger/codegen/config/ReservedWordMapper.java b/src/main/java/com/wordnik/swagger/codegen/config/ReservedWordMapper.java new file mode 100644 index 00000000000..db40063f126 --- /dev/null +++ b/src/main/java/com/wordnik/swagger/codegen/config/ReservedWordMapper.java @@ -0,0 +1,21 @@ +/** + * Copyright 2011 Wordnik, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.wordnik.swagger.codegen.config; + +public interface ReservedWordMapper { + public String translate(String input); +} diff --git a/src/main/java/com/wordnik/swagger/codegen/config/common/UnderscoreNamingPolicyProvider.java b/src/main/java/com/wordnik/swagger/codegen/config/common/UnderscoreNamingPolicyProvider.java new file mode 100644 index 00000000000..30fbd0c01f4 --- /dev/null +++ b/src/main/java/com/wordnik/swagger/codegen/config/common/UnderscoreNamingPolicyProvider.java @@ -0,0 +1,198 @@ +/** + * Copyright 2011 Wordnik, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.wordnik.swagger.codegen.config.common; + +import com.wordnik.swagger.codegen.exception.CodeGenerationException; +import com.wordnik.swagger.codegen.config.NamingPolicyProvider; + +/** + * User: ramesh + * Date: 5/31/11 + * Time: 7:03 AM + */ +public class UnderscoreNamingPolicyProvider implements NamingPolicyProvider { + public static String INPUT_OBJECT_SUFFIX = "Input"; + + String convertToUnderscoreName(String input){ + if(null==input) return null; + + return input.replaceAll( + String.format("%s|%s|%s", + "(?<=[A-Z])(?=[A-Z][a-z])", + "(?<=[^A-Z])(?=[A-Z])", + "(?<=[A-Za-z])(?=[^A-Za-z])"), + "_").toLowerCase(); + } + + /** + * gets the name of class that is responsible for tracking current library version + * @return + */ + public String getVersionCheckerClassName() { + return "version_checker"; + } + + /** + * Converts the first character of the input into upper case . + * Example: If the input is word, the return value will be Word + * @param input + * @return + */ + public String applyClassNamingPolicy(String input) { + if(input != null && input.length() > 0) { + return convertToUnderscoreName(input); + }else{ + throw new CodeGenerationException("Error converting input to first letter caps becuase of null or empty input"); + } + } + + /** + * Converts the first character of the input into lower case. + * Example: If the input is GetWord, the return value will be getWord + * @param input + * @return + */ + public String applyMethodNamingPolicy(String input) { + if(input != null && input.length() > 0) { + return convertToUnderscoreName(input); + }else{ + throw new CodeGenerationException("Error converting input to first letter to lower because of null or empty input"); + } + } + + + /** + * Generate name of the service from resource path. + * + * Example: if input is /user.json the generated name for this path will be UserAPI + * If the input is /user.json/{userId}, the service name will still be generated as UserAPI + * + * @param resourcePath + * @return + */ + public String getServiceName(String resourcePath) { + String className = null; + int index = resourcePath.indexOf("."); + if(index >= 0) { + String resourceName = resourcePath.substring(1,index); + className = applyClassNamingPolicy(resourceName); + }else{ + String[] paths = resourcePath.split("/"); + for(String path : paths) { + if(path != null && path.length() > 0) { + className = applyClassNamingPolicy(path); + break; + } + } + } + return className + "_api"; + } + + /** + * Generates the name of service methods. + * + * Resource documentation provides suggested names. Individual language can choose to use suggested name or + * generate the name based on end point path. Example: IN java we use suggested name + * + * @param endPoint + * @param suggestedName + * @return + */ + public String getMethodName(String endPoint, String suggestedName) { + return convertToUnderscoreName(suggestedName); + } + + + /** + * For input UserAPI and resource path /findUserById the suggested input object name will be: UserFindUserByIdInput + * + * If the input path is /{userId}/delete the suggested name will be UserDeleteInput. The path parameters are ignored + * in generating the input object name + * + * Note: Input objects are only created when the number of input arguments in a method exceeds certain number so
that the method signatures are clean + * + * + * @param serviceName + * @param resourcePath + * @return + */ + public String getInputObjectName(String serviceName, String resourcePath) { + + //Since service name has API at the end remove that format he name + String inputobjectName = serviceName.substring(0, serviceName.length() - 3); + + String[] pathElements = resourcePath.split("/"); + StringBuilder urlPath = new StringBuilder(""); + if(pathElements != null){ + for(int i=0; i < pathElements.length; i++){ + String pathElement = pathElements[i]; + if(pathElement != null && pathElement.length() > 0) { + int position = pathElement.indexOf("{"); + if(position < 0) { + inputobjectName = inputobjectName + applyClassNamingPolicy(pathElement) + INPUT_OBJECT_SUFFIX; + } + } + } + } + return inputobjectName; + } + + /** + * Generate the name of the wrapper class used as a wrapper for a list of items returned by a service + *

+ * Example: get definitions API returns a list of definition objects as the result. This will be wrapped by an + * object. The object's name will be determined by invoking this service. + * eg. DefinitionList for a wrapper of 'definition's + * + * @param wrapperItemName + * @return + */ + public String getListWrapperName(String wrapperItemName) { + return applyClassNamingPolicy(wrapperItemName) + "_list"; + } + + /** + * Generates a name for an enum for the param or field name. + *

+ * Example: for a param source the return could be SourceEnum + * + * @param input + * @return + */ + public String getEnumName(String input) { + if (input != null && input.length() > 0) { + return this.applyClassNamingPolicy(input).concat("_values"); + } else { + throw new CodeGenerationException("Error getting Enum name becuase of null input"); + } + } + + /** + * Gets the signature of the method that gets value for give attribute name. + * + * Example: If class name is user and attibute name is email the out in java language will be + * user.getEmail() + * + * @param className + * @param attributeName + * @return + */ + public String createGetterMethodName(String className, String attributeName) { + return className+".get_"+ applyClassNamingPolicy(attributeName)+"()"; + } + +} diff --git a/src/main/java/com/wordnik/swagger/codegen/config/scala/ScalaLibCodeGen.scala b/src/main/java/com/wordnik/swagger/codegen/config/scala/ScalaLibCodeGen.scala index cf10db75ba1..1440588e379 100644 --- a/src/main/java/com/wordnik/swagger/codegen/config/scala/ScalaLibCodeGen.scala +++ b/src/main/java/com/wordnik/swagger/codegen/config/scala/ScalaLibCodeGen.scala @@ -59,12 +59,12 @@ class ScalaLibCodeGen( libraryHome: String, configPath: String) extends LibraryCodeGenerator { + this.reservedWordMapper = new ScalaReservedWordMapper if (null != configPath) { initializeWithConfigPath(configPath) this.setDataTypeMappingProvider(new ScalaDataTypeMappingProvider()) this.setNameGenerator(new CamelCaseNamingPolicyProvider()) - } - else{ + } else { initialize(apiServerURL, apiKey, modelPackageName, apiPackageName, classOutputDir, libraryHome) setDataTypeMappingProvider(new ScalaDataTypeMappingProvider()) setNameGenerator(new CamelCaseNamingPolicyProvider()) diff --git a/src/main/java/com/wordnik/swagger/codegen/config/scala/ScalaReservedWordMapper.scala b/src/main/java/com/wordnik/swagger/codegen/config/scala/ScalaReservedWordMapper.scala new file mode 100644 index 00000000000..825e696cb6a --- /dev/null +++ b/src/main/java/com/wordnik/swagger/codegen/config/scala/ScalaReservedWordMapper.scala @@ -0,0 +1,32 @@ +/** + * Copyright 2011 Wordnik, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.wordnik.swagger.codegen.config.scala + +import com.wordnik.swagger.codegen.config.ReservedWordMapper + +object ScalaKeywordMapper { + val reservedWords = Array("type", "case") +} + +class ScalaReservedWordMapper extends ReservedWordMapper { + override def translate(input: String): String = { + ScalaKeywordMapper.reservedWords.contains(input) match { + case true => "`" + input + "`" + case false => input + } + } +} diff --git a/src/main/java/com/wordnik/swagger/codegen/resource/Model.java b/src/main/java/com/wordnik/swagger/codegen/resource/Model.java index ef481f50286..b8984a834bc 100644 --- a/src/main/java/com/wordnik/swagger/codegen/resource/Model.java +++ b/src/main/java/com/wordnik/swagger/codegen/resource/Model.java @@ -24,11 +24,8 @@ * Time: 8:31 AM */ public class Model { - private String name; - private String description; - private List fields; public String getName() { diff --git a/src/main/java/com/wordnik/swagger/codegen/resource/ModelField.java b/src/main/java/com/wordnik/swagger/codegen/resource/ModelField.java index 1ae88672136..ea5393b8c06 100644 --- a/src/main/java/com/wordnik/swagger/codegen/resource/ModelField.java +++ b/src/main/java/com/wordnik/swagger/codegen/resource/ModelField.java @@ -31,29 +31,17 @@ * Time: 7:57 AM */ public class ModelField { - private String name; - private String wrapperName; - private String description = ""; - private String defaultValue; - private boolean required = false; - private boolean allowMultiple = false; - private List allowableValues = null; - private String paramType; - private String dataType; - private String internalDescription; - private String paramAccess; - private FieldDefinition fieldDefinition; public String getName() { diff --git a/src/main/java/com/wordnik/swagger/codegen/resource/Resource.java b/src/main/java/com/wordnik/swagger/codegen/resource/Resource.java index c479664ef70..397c69db060 100644 --- a/src/main/java/com/wordnik/swagger/codegen/resource/Resource.java +++ b/src/main/java/com/wordnik/swagger/codegen/resource/Resource.java @@ -32,10 +32,8 @@ * Time: 7:01 PM */ public class Resource { - private String apiVersion; - //TODO rename the JSON property too after the sandbox var has been renamed @JsonProperty("swaggerVersion") private String swaggerVersion; @@ -48,11 +46,8 @@ public class Resource { @JsonProperty("models") private ApiModelListWrapper modelListWrapper; - private List models = new ArrayList(); - private String generatedClassName; - private List methods; @JsonCreator @@ -117,13 +112,13 @@ public List getModels() { }*/ public String generateClassName(NamingPolicyProvider nameGenerator) { - if (generatedClassName == null) { + if (generatedClassName == null && endPoints.size() > 0) { String endPointPath = endPoints.get(0).getPath(); generatedClassName = nameGenerator.getServiceName(endPointPath); - } + } return generatedClassName; } - + public List generateMethods(Resource resource, DataTypeMappingProvider dataTypeMapper, NamingPolicyProvider nameGenerator) { if(methods == null){ methods = new ArrayList(); @@ -146,7 +141,5 @@ public void generateModelsFromWrapper(NamingPolicyProvider nameGenerator) { models.add (modelDefn.toModel(modelName, nameGenerator) ); } } - } - }