diff --git a/.gitignore b/.gitignore index e676162d04f..65c86663fdf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,28 +1,7 @@ -java/wordnik*.zip -java/*.ipr -java/*.iml -java/*.iws -java/dist/ -java/build/ -java/lib/*.jar -java/META-INF/ -java/web/ -java/index -java/reports/ -java/lib/*.zip -java/logs -java/src/main/java/com/wordnik/env/Version.scala -java/lib/*.pom -java/version.properties -java/out -android/codegen/build/ *.iml out/ *.ipr *.iws version.properties -android/codegen/lib/ -android/driver/lib/ -android/driver/build/ -android/driver-test/build/ -android/driver-test/lib/*.jar +lib/* +build/* \ No newline at end of file diff --git a/bin/runjava.sh b/bin/runjava.sh new file mode 100755 index 00000000000..ad6d8a172a3 --- /dev/null +++ b/bin/runjava.sh @@ -0,0 +1,17 @@ +#!/bin/bash +echo "" > classpath.txt +for file in `ls lib`; + do echo -n 'lib/' >> classpath.txt; + echo -n $file >> classpath.txt; + echo -n ':' >> classpath.txt; +done +for file in `ls build`; + do echo -n 'build/' >> classpath.txt; + echo -n $file >> classpath.txt; + echo -n ':' >> classpath.txt; +done + +export CLASSPATH=$(cat classpath.txt) +export JAVA_OPTS="${JAVA_OPTS} -DrulePath=data -Dproperty=Xmx2g -DloggerPath=$BUILD_COMMON/test-config/log4j.properties" +java $WORDNIK_OPTS $JAVA_CONFIG_OPTIONS $JAVA_OPTS -cp $CLASSPATH "$@" + diff --git a/bin/runscala.sh b/bin/runscala.sh new file mode 100755 index 00000000000..454db86c955 --- /dev/null +++ b/bin/runscala.sh @@ -0,0 +1,17 @@ +#!/bin/bash +echo "" > classpath.txt +for file in `ls lib`; + do echo -n 'lib/' >> classpath.txt; + echo -n $file >> classpath.txt; + echo -n ':' >> classpath.txt; +done +for file in `ls build`; + do echo -n 'build/' >> classpath.txt; + echo -n $file >> classpath.txt; + echo -n ':' >> classpath.txt; +done + +export CLASSPATH=$(cat classpath.txt) +export JAVA_OPTS="${JAVA_OPTS} -DrulePath=data -Dproperty=Xmx2g -DloggerPath=$BUILD_COMMON/test-config/log4j.properties" +scala $WORDNIK_OPTS $JAVA_CONFIG_OPTIONS $JAVA_OPTS -cp $CLASSPATH "$@" + diff --git a/build.xml b/build.xml new file mode 100644 index 00000000000..5ede0f39312 --- /dev/null +++ b/build.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must specify the parameters: outputPath and apiServerRootDir eg. -DoutputPath=../../api-server-lib/java/src/main/java/com/wordnik/ + -DapiServerRootDir=../../api-server-lib/java/ + These are the output path of the Apis and the root directory that will be created for the api server (directory where structure is created) + + + + + outputPath for Api = ${outputPath} + + + apiServerRootDir = ${apiServerRootDir} + + + + + + + + + + + + + + + + + + + + diff --git a/conf/java/structure/build.xml b/conf/java/structure/build.xml new file mode 100644 index 00000000000..af23a0a1d32 --- /dev/null +++ b/conf/java/structure/build.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/conf/java/structure/ivy.xml b/conf/java/structure/ivy.xml new file mode 100644 index 00000000000..ec44869bb26 --- /dev/null +++ b/conf/java/structure/ivy.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/conf/java/structure/src/main/java/com/wordnik/swagger/annotations/AllowableValues.java b/conf/java/structure/src/main/java/com/wordnik/swagger/annotations/AllowableValues.java new file mode 100644 index 00000000000..bc162b068ec --- /dev/null +++ b/conf/java/structure/src/main/java/com/wordnik/swagger/annotations/AllowableValues.java @@ -0,0 +1,19 @@ +package com.wordnik.swagger.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + + +/** + * Annotation used to provide list of possible values + * @author ramesh + * + */ +@Target({ElementType.FIELD,ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface AllowableValues { + + String value() default ""; +} diff --git a/conf/java/structure/src/main/java/com/wordnik/swagger/annotations/MethodArgumentNames.java b/conf/java/structure/src/main/java/com/wordnik/swagger/annotations/MethodArgumentNames.java new file mode 100644 index 00000000000..01aaac0093f --- /dev/null +++ b/conf/java/structure/src/main/java/com/wordnik/swagger/annotations/MethodArgumentNames.java @@ -0,0 +1,13 @@ +package com.wordnik.swagger.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + + +@Target({ElementType.FIELD,ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface MethodArgumentNames { + String value() default ""; +} diff --git a/conf/java/structure/src/main/java/com/wordnik/swagger/annotations/Required.java b/conf/java/structure/src/main/java/com/wordnik/swagger/annotations/Required.java new file mode 100644 index 00000000000..e3fa6402548 --- /dev/null +++ b/conf/java/structure/src/main/java/com/wordnik/swagger/annotations/Required.java @@ -0,0 +1,17 @@ +package com.wordnik.swagger.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation used to indicate given property or field is required or not + * @author ramesh + * + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Required { + +} diff --git a/conf/java/structure/src/main/java/com/wordnik/swagger/common/StringValue.java b/conf/java/structure/src/main/java/com/wordnik/swagger/common/StringValue.java new file mode 100644 index 00000000000..4c7eaef40f0 --- /dev/null +++ b/conf/java/structure/src/main/java/com/wordnik/swagger/common/StringValue.java @@ -0,0 +1,22 @@ +package com.wordnik.swagger.common; + +/** + * Created by IntelliJ IDEA. + * User: ramesh + * Date: 4/12/11 + * Time: 5:46 PM + * To change this template use File | Settings | File Templates. + */ +public class StringValue { + + String word; + + + public String getWord() { + return word; + } + + public void setWord(String value) { + this.word = value; + } +} diff --git a/conf/java/structure/src/main/java/com/wordnik/swagger/common/WordnikAPI.java b/conf/java/structure/src/main/java/com/wordnik/swagger/common/WordnikAPI.java new file mode 100644 index 00000000000..011f1423dbf --- /dev/null +++ b/conf/java/structure/src/main/java/com/wordnik/swagger/common/WordnikAPI.java @@ -0,0 +1,216 @@ +package com.wordnik.swagger.common; + +import java.io.IOException; +import java.util.Map; +import java.util.logging.Logger; + +import javax.ws.rs.core.MultivaluedMap; + +import com.wordnik.swagger.exception.APIException; +import com.wordnik.swagger.exception.APIExceptionCodes; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.map.DeserializationConfig.Feature; +import org.codehaus.jackson.map.SerializationConfig; +import org.codehaus.jackson.type.TypeReference; + +import com.sun.jersey.api.client.Client; +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.client.WebResource; +import com.sun.jersey.api.client.WebResource.Builder; +import com.sun.jersey.api.client.filter.LoggingFilter; + + +/** + * Provides way to initialize the communication with Wordnik API server. + * This is also a Base class for all API classes + * @author ramesh + * + */ +public class WordnikAPI { + + private static String apiServer = "http://api.wordnik.com/v4"; + private static String apiKey = ""; + private static boolean loggingEnabled; + private static Logger logger = null; + + protected static String POST = "POST"; + protected static String GET = "GET"; + protected static String PUT = "PUT"; + protected static String DELETE = "DELETE"; + protected static ObjectMapper mapper = new ObjectMapper(); + static{ + mapper.getDeserializationConfig().set(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false); + mapper.getSerializationConfig().set(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false); + mapper.configure(SerializationConfig.Feature.WRITE_NULL_PROPERTIES, false); + mapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false); + } + + /** + * Initializes the API communication with required inputs. + * @param apiKey provide the key provided as part of registration + * @param apiServer Sets the URL for the API server. It is defaulted to the server + * used while building the driver. This value should be provided while testing the APIs against + * test servers or if there is any changes in production server URLs. + * @param enableLogging This will enable the logging using Jersey logging filter. Refer the following documentation + * for more details. {@link com.sun.jersey.api.client.filter.LoggingFilter}. Default output is sent to system.out. + * Create a logger ({@link java.util.logging.Logger} class and set using setLogger method. + */ + public static void initialize(String apiKey, String apiServer, boolean enableLogging) { + setApiKey(apiKey); + if(apiServer != null && apiServer.length() > 0) { + if(apiServer.substring(apiServer.length()-1).equals("/")){ + apiServer = apiServer.substring(0, apiServer.length()-1); + } + setApiServer(apiServer); + } + loggingEnabled = enableLogging; + } + + /** + * Set the logger instance used for Jersey logging. + * @param aLogger + */ + public static void setLogger(Logger aLogger) { + logger = aLogger; + } + + /** + * Gets the API key used for server communication. + * This value is set using initialize method. + * @return + */ + public static String getApiKey() { + return apiKey; + } + + private static void setApiKey(String apiKey) { + WordnikAPI.apiKey = apiKey; + } + + /** + * Sets the URL for the API server. It is defaulted to the server used while building the driver. + * @return + */ + private static String getApiServer() { + return apiServer; + } + + private static void setApiServer(String server) { + WordnikAPI.apiServer = server; + } + + + + /** + * Invokes the API and returns the response as json string. + * This is an internal method called by individual APIs for communication. It sets the required HTTP headers + * based on API key and auth token. + * @param authToken - token that is received as part of authentication call. This is only needed for the calls that are secure. + * @param resourceURL - URL for the rest resource + * @param method - Method we should use for communicating to the back end. + * @param postObject - if the method is POST, provide the object that should be sent as part of post request. + * @return JSON response of the API call. + * @throws com.wordnik.swagger.exception.APIException if the call to API server fails. + */ + protected static String invokeAPI(String authToken, String resourceURL, String method, Map queryParams, Object postObject) throws APIException { + + + Client apiClient = Client.create(); + + //check for app key and server values + if(getApiKey() == null || getApiKey().length() == 0) { + String[] args = {getApiKey()}; + throw new APIException(APIExceptionCodes.API_KEY_NOT_VALID, args); + } + if(getApiServer() == null || getApiServer().length() == 0) { + String[] args = {getApiServer()}; + throw new APIException(APIExceptionCodes.API_SERVER_NOT_VALID, args); + } + //initialize the logger if needed + if(loggingEnabled) { + if(logger == null) { + apiClient.addFilter(new LoggingFilter()); + }else{ + apiClient.addFilter(new LoggingFilter(logger)); + } + } + + //make the communication + resourceURL = getApiServer() + resourceURL; + if(queryParams.keySet().size() > 0){ + int i=0; + for(String paramName : queryParams.keySet()){ + String symbol = "&"; + if(i==0){ + symbol = "?"; + } + resourceURL = resourceURL + symbol + paramName + "=" + queryParams.get(paramName); + i++; + } + } + WebResource aResource = apiClient.resource(resourceURL); + // aResource.queryParams(queryParams); + + //set the required HTTP headers + Builder builder = aResource.type("application/json"); + builder.header("api_key", getApiKey()); + if(authToken != null){ + builder.header("auth_token", authToken); + } + ClientResponse clientResponse = null; + if(method.equals(GET)) { + clientResponse = builder.get(ClientResponse.class); + }else if (method.equals(POST)) { + clientResponse = builder.post(ClientResponse.class, serialize(postObject)); + }else if (method.equals(PUT)) { + clientResponse = builder.put(ClientResponse.class, serialize(postObject)); + }else if (method.equals(DELETE)) { + clientResponse = builder.delete(ClientResponse.class); + } + + //process the response + if(clientResponse.getClientResponseStatus() == ClientResponse.Status.OK) { + String response = clientResponse.getEntity(String.class); + return response; + }else{ + int responseCode = clientResponse.getClientResponseStatus().getStatusCode() ; + throw new APIException(responseCode, clientResponse.getEntity(String.class)); + } + } + + /** + * De-serialize the object from String to object of type input class name. + * @param response + * @param inputClassName + * @return + */ + public static Object deserialize(String response, Class inputClassName) throws APIException { + try { + System.out.println("Input :::::" + response); + Object responseObject = mapper.readValue(response, inputClassName); + return responseObject; + } catch (IOException ioe) { + String[] args = new String[]{response, inputClassName.toString()}; + throw new APIException(APIExceptionCodes.ERROR_CONVERTING_JSON_TO_JAVA, args, "Error in coversting response json value to java object : " + ioe.getMessage(), ioe); + } + } + + + /** + * serialize the object from String to input object. + * @param input + * @return + */ + public static String serialize(Object input) throws APIException { + try { + if(input != null) { + return mapper.writeValueAsString(input); + }else{ + return ""; + } + } catch (IOException ioe) { + throw new APIException(APIExceptionCodes.ERROR_CONVERTING_JAVA_TO_JSON, "Error in coverting input java to json : " + ioe.getMessage(), ioe); + } + } +} diff --git a/conf/java/structure/src/main/java/com/wordnik/swagger/exception/APIException.java b/conf/java/structure/src/main/java/com/wordnik/swagger/exception/APIException.java new file mode 100644 index 00000000000..61e98e24326 --- /dev/null +++ b/conf/java/structure/src/main/java/com/wordnik/swagger/exception/APIException.java @@ -0,0 +1,84 @@ +package com.wordnik.swagger.exception; + +import com.sun.jersey.api.client.ClientResponse; +import org.codehaus.jackson.annotate.JsonAutoDetect; +import org.codehaus.jackson.annotate.JsonCreator; +import org.codehaus.jackson.annotate.JsonMethod; +import org.codehaus.jackson.annotate.JsonProperty; + +/** + * Exception that is thrown if there are any issues in invoking Wordnik API. + * + * Each exception carries a code and message. Code can be either HTTP error response code {@link com.sun.jersey.api.client.ClientResponse.Status} + * or The list of possible Wordnik exception code that are listed in the interface {@link APIExceptionCodes}. + * User: ramesh + * Date: 3/31/11 + * Time: 9:27 AM + */ +public class APIException extends Exception { + + private String message; + + private int code; + + private String[] args; + + @JsonCreator + public APIException() { + } + + public APIException(String message) { + super(message); + } + + public APIException(int code) { + this.code = code; + } + + public APIException(int code, String message, Throwable t) { + super(message, t); + this.message = message; + this.code = code; + } + + public APIException(int code, String[] args, String message, Throwable t) { + super(message, t); + this.message = message; + this.code = code; + this.args = args; + } + + public APIException(int code, String message) { + super(message); + this.message = message; + this.code = code; + } + + public APIException(int code, String[] args, String message) { + super(message); + this.message = message; + this.code = code; + this.args = args; + } + + public APIException(int code, String[] args) { + this.code = code; + this.args = args; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } +} diff --git a/conf/java/structure/src/main/java/com/wordnik/swagger/exception/APIExceptionCodes.java b/conf/java/structure/src/main/java/com/wordnik/swagger/exception/APIExceptionCodes.java new file mode 100644 index 00000000000..688f7342f98 --- /dev/null +++ b/conf/java/structure/src/main/java/com/wordnik/swagger/exception/APIExceptionCodes.java @@ -0,0 +1,38 @@ +package com.wordnik.swagger.exception; + +/** + * Lists all the possible exception codes + * @author ramesh + * + */ +public interface APIExceptionCodes { + + /** + * System exception. + */ + public static final int SYSTEM_EXCEPTION = 0; + + /** + * With Arguments as current key. + */ + public static final int API_KEY_NOT_VALID = 1000; + /** + * With arguments as current token value + */ + public static final int AUTH_TOKEN_NOT_VALID = 1001; + /** + * With arguments as input JSON and output class anme + */ + public static final int ERROR_CONVERTING_JSON_TO_JAVA = 1002; + /** + * With arguments as JAVA class name + */ + public static final int ERROR_CONVERTING_JAVA_TO_JSON = 1003; + + public static final int ERROR_FROM_WEBSERVICE_CALL = 1004; + /** + * With arguments as current API server name + */ + public static final int API_SERVER_NOT_VALID = 1005; + +} diff --git a/conf/java/structure/src/main/java/com/wordnik/swagger/exception/CodeGenerationException.java b/conf/java/structure/src/main/java/com/wordnik/swagger/exception/CodeGenerationException.java new file mode 100644 index 00000000000..308f4a27338 --- /dev/null +++ b/conf/java/structure/src/main/java/com/wordnik/swagger/exception/CodeGenerationException.java @@ -0,0 +1,25 @@ +package com.wordnik.swagger.exception; + +/** + * Exception raised while generating code for java driver. + * User: ramesh + * Date: 3/31/11 + * Time: 9:29 AM + */ +public class CodeGenerationException extends RuntimeException { + + private String message; + + public CodeGenerationException(String message){ + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} + diff --git a/conf/java/templates/EnumObject.st b/conf/java/templates/EnumObject.st new file mode 100644 index 00000000000..9cbc2c661c5 --- /dev/null +++ b/conf/java/templates/EnumObject.st @@ -0,0 +1,30 @@ +package $packageName$; + +$imports:{ import | +import $import$; +}$ + +/** + * $enum.description$ + * NOTE: This class is auto generated by the drive code generator program so please do not edit the class manually. + * @author deepak + * + */ +public enum $className$ { + + $values: { value | $value.name$($value.value$)};separator=", "$; + + final $enumValueType$ value; + + $className$($enumValueType$ value) { + this.value = value; + } + + public $enumValueType$ getValue() { + return value; + } + + @Override public String toString() { + return String.valueOf(this.getValue()); + } +}; \ No newline at end of file diff --git a/conf/java/templates/ModelObject.st b/conf/java/templates/ModelObject.st new file mode 100644 index 00000000000..75ebd5a1999 --- /dev/null +++ b/conf/java/templates/ModelObject.st @@ -0,0 +1,40 @@ +package $packageName$; + +import $annotationPackageName$.AllowableValues; +import $annotationPackageName$.Required; + +$imports:{ import | +import $import$; +}$ + +/** + * $model.description$ + * NOTE: This class is auto generated by the drive code generator program so please do not edit the class manually. + * @author ramesh + * + */ +public class $className$ extends $extends$ { + +$fields:{ field | + + /** + * $field.description$ + */ + private $field.fieldDefinition.returnType$ $field.fieldDefinition.name$ $field.fieldDefinition.initialization$;$\r$}$ + +$fields:{ field | + /** + * $field.description$ + * $if(field.required)$@Required$endif$ + * $if(field.allowableValues)$@AllowableValues(value="$field.allowedValuesString$")$endif$ + */ + public $field.fieldDefinition.returnType$ get$field.fieldDefinition.NameForMethod$() { + return $field.fieldDefinition.name$; + } + + public void set$field.fieldDefinition.NameForMethod$($field.fieldDefinition.returnType$ $field.fieldDefinition.name$) { + this.$field.fieldDefinition.name$ = $field.fieldDefinition.name$; + } + +}$ +} \ No newline at end of file diff --git a/conf/java/templates/ResourceObject.st b/conf/java/templates/ResourceObject.st new file mode 100644 index 00000000000..3887c24b919 --- /dev/null +++ b/conf/java/templates/ResourceObject.st @@ -0,0 +1,153 @@ +package $packageName$; + + +import $annotationPackageName$.MethodArgumentNames; +import $exceptionPackageName$.APIExceptionCodes; +import $exceptionPackageName$.APIException; +import $modelPackageName$.*; + +import org.codehaus.jackson.map.DeserializationConfig.Feature; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.type.TypeReference; + +import java.util.*; +import java.io.IOException; + +$imports:{ import | +import $import$; +}$ + +/** + * NOTE: This class is auto generated by the drive code generator program so please do not edit the class manually. + * @author ramesh + * + */ +public class $resource$ extends $extends$ { + +$methods:{ method | + /** + * $method.description$ +$method.arguments:{ argument | + * @param $argument.name$ $argument.description$ + $if(argument.allowedValues)$ + * Allowed values are - $argument.allowedValues$ + $endif$ +}$ + * +$if(!method.responseVoid)$ + * @return $method.returnValue$ {@link $method.returnClassName$} $endif$ + * @throws APIException $method.exceptionDescription$ + */ +$if(method.hasArguments)$ + @MethodArgumentNames(value="$method.argumentNames; separator=", "$") +$endif$ + + public static $method.returnValue$ $method.name$($method.argumentDefinitions; separator=", "$) throws APIException { + +$if(method.authToken)$ + if(authToken == null || authToken.length() == 0) { + throw new APIException(APIExceptionCodes.AUTH_TOKEN_NOT_VALID); + } +$endif$ + + //parse inputs + String resourcePath = "$method.resourcePath$"; + resourcePath = resourcePath.replace("{format}","json"); + String method = "$method.methodType$"; + Map queryParams = new HashMap(); +$if(!method.inputModel)$ +$method.queryParameters:{ argument | + if( $argument.name$ != null) { + queryParams.put("$argument.name$", toPathValue($argument.name$)); + } +}$ +$method.pathParameters:{ argument | + if( $argument.name$ != null) { + resourcePath = resourcePath.replace("{$argument.name$}", $argument.name$); + } +}$ +$endif$ +$if(method.inputModel)$ +$method.queryParameters:{ argument | + if( $argument.inputModelClassArgument$ != null && $argument.methodNameFromModelClass$ != null) { + queryParams.put("$argument.name$", $argument.methodNameFromModelClass$); + } +}$ +$method.pathParameters:{ argument | + if( $argument.inputModelClassArgument$ != null && $argument.methodNameFromModelClass$ != null) { + resourcePath = resourcePath.replace("{$argument.name$}", $argument.methodNameFromModelClass$); + } +}$ +$endif$ + //make the API Call +$if(method.postObject)$ +$if(method.authToken)$ + String response = invokeAPI(authToken, resourcePath, method, queryParams, postObject); +$endif$ +$if(!method.authToken)$ + String response = invokeAPI(null, resourcePath, method, queryParams, postObject); +$endif$ +$endif$ + +$if(!method.postObject)$ +$if(method.authToken)$ + String response = invokeAPI(authToken, resourcePath, method, queryParams, null); +$endif$ +$if(!method.authToken)$ + String response = invokeAPI(null, resourcePath, method, queryParams, null); +$endif$ +$endif$ + + //create output objects if the response has more than one object +$if(!method.responseVoid)$ + if(response == null || response.length() == 0){ + return null; + } +$if(!method.returnValueList)$ + $method.returnValue$ responseObject = ($method.returnValue$)deserialize(response, $method.returnClassName$.class); + return responseObject; +$endif$ +$if(method.returnValueList)$ + TypeReference> typeRef = new TypeReference>() { + }; + try { + List<$method.returnClassName$> responseObject = (List<$method.returnClassName$>) mapper.readValue(response, typeRef); + return responseObject; + } catch (IOException ioe) { + String[] args = new String[]{response, typeRef.toString()}; + throw new APIException(APIExceptionCodes.ERROR_CONVERTING_JSON_TO_JAVA, args, "Error in converting response json value to java object : " + ioe.getMessage(), ioe); + } +$endif$ +$endif$ + } + +}$ + + /** + * Overloaded method for returning the path value + * For a string value an empty value is returned if the value is null + * @param value + * @return + */ + private static String toPathValue(String value) { + return value == null ? "" : value; + } + + /** + * Overloaded method for returning a path value + * For a list of objects a comma separated string is returned + * @param objects + * @return + */ + private static String toPathValue(List objects) { + StringBuilder out = new StringBuilder(); + for(Object o: objects){ + out.append(o.toString()); + out.append(","); + } + if(out.indexOf(",") != -1) { + return out.substring(0, out.lastIndexOf(",") ); + } + return out.toString(); + } +} diff --git a/conf/java/templates/VersionChecker.st b/conf/java/templates/VersionChecker.st new file mode 100644 index 00000000000..1044bd190cb --- /dev/null +++ b/conf/java/templates/VersionChecker.st @@ -0,0 +1,18 @@ +package $packageName$; + +/** + * Maintains the compatible server version against which the drive is written + * @author ramesh + * + */ +public class VersionChecker { + + private String compatibleVersion = "$apiVersion$"; + + /** + * Gets the version against which the driver code was written + */ + public String getCompatibleVersion() { + return compatibleVersion; + } +} \ No newline at end of file diff --git a/ivy.xml b/ivy.xml new file mode 100644 index 00000000000..7a0ee256653 --- /dev/null +++ b/ivy.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/com/wordnik/swagger/codegen/DriverCodeGenerator.java b/src/main/java/com/wordnik/swagger/codegen/DriverCodeGenerator.java new file mode 100644 index 00000000000..ff21366d21c --- /dev/null +++ b/src/main/java/com/wordnik/swagger/codegen/DriverCodeGenerator.java @@ -0,0 +1,336 @@ +package com.wordnik.swagger.codegen; + +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.resource.*; +import com.wordnik.swagger.exception.CodeGenerationException; +import org.antlr.stringtemplate.StringTemplate; +import org.antlr.stringtemplate.StringTemplateGroup; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * User: ramesh + * Date: 3/30/11 + * Time: 6:59 PM + */ +public class DriverCodeGenerator { + + private static String VERSION_OBJECT_TEMPLATE = "VersionChecker"; + private static String MODEL_OBJECT_TEMPLATE = "ModelObject"; + private static String API_OBJECT_TEMPLATE = "ResourceObject"; + private static final String ENUM_OBJECT_TEMPLATE = "EnumObject"; + + private static final String PACKAGE_NAME = "packageName"; + private ApiConfiguration config = null; + private LanguageConfiguration languageConfig = null; + + private SwaggerResourceDocReader apiMarshaller; + protected DataTypeMappingProvider dataTypeMappingProvider; + protected RulesProvider codeGenRulesProvider; + protected NamingPolicyProvider nameGenerator; + + /** + * Generate classes needed for the model and API invocation + */ + public void generateCode() { + apiMarshaller = new SwaggerResourceDocReader(this.config, this.getDataTypeMappingProvider(), this.getNameGenerator()); + //read resources and get their documentation + List resources = apiMarshaller.readResourceDocumentation(); + StringTemplateGroup aTemplateGroup = new StringTemplateGroup("templates", languageConfig.getTemplateLocation()); + if(resources.size() > 0) { + generateVersionHelper(resources.get(0).getApiVersion(), aTemplateGroup); + } + generateModelClasses(resources, aTemplateGroup); + generateModelClassesForInput(resources, aTemplateGroup); + generateEnumForAllowedValues(resources, aTemplateGroup); + generateAPIClasses(resources, aTemplateGroup); + } + + /** + * 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 + */ + private void generateVersionHelper(String version, StringTemplateGroup templateGroup) { + StringTemplate template = templateGroup.getInstanceOf(VERSION_OBJECT_TEMPLATE); + template.setAttribute("apiVersion", version); + template.setAttribute(PACKAGE_NAME, config.getApiPackageName()); + File aFile = new File(languageConfig.getResourceClassLocation() + this.getNameGenerator().getVersionCheckerClassName() + + languageConfig.getClassFileExtension()); + writeFile(aFile, template.toString(), "Version checker class"); + } + + /** + * Generates model classes. If the class is already generated then ignores the same. + */ + private void generateModelClasses(List resources, StringTemplateGroup templateGroup) { + List generatedClassNames = new ArrayList(); + + for(Resource resource: resources) { + for(Model model : resource.getModels()){ + if(!generatedClassNames.contains(model.getName()) && !this.getCodeGenRulesProvider().isModelIgnored(model.getName())){ + List imports = new ArrayList(); + imports.addAll(this.config.getDefaultModelImports()); + for(ModelField param : model.getFields()){ + for(String importDef : param.getFieldDefinition(this.getDataTypeMappingProvider()).getImportDefinitions()){ + if(!imports.contains(importDef)){ + imports.add(importDef); + } + } + } + StringTemplate template = templateGroup.getInstanceOf(MODEL_OBJECT_TEMPLATE); + template.setAttribute("fields", model.getFields()); + template.setAttribute("imports", imports); + template.setAttribute("annotationPackageName", languageConfig.getAnnotationPackageName()); + template.setAttribute("extends", config.getModelBaseClass()); + template.setAttribute("className", model.getGenratedClassName()); + template.setAttribute(PACKAGE_NAME, config.getModelPackageName()); + File aFile = new File(languageConfig.getModelClassLocation()+model.getGenratedClassName()+languageConfig.getClassFileExtension()); + writeFile(aFile, template.toString(), "Model class"); + generatedClassNames.add(model.getName()); + } + } + } + + generateWrapperClassForTestData(generatedClassNames, templateGroup); + } + + /** + * Generates assembler classes if the API returns more than one objects. + * @param resources + * @param templateGroup + */ + private void generateModelClassesForInput(List resources, StringTemplateGroup templateGroup) { + List generatedClasses = new ArrayList(); + for(Resource resource : resources) { + if(resource.getEndPoints() != null) { + for(Endpoint endpoint : resource.getEndPoints()){ + if(endpoint.getOperations() != null) { + for(EndpointOperation operation : endpoint.getOperations()){ + ResourceMethod method = operation.generateMethod(endpoint, resource, dataTypeMappingProvider, nameGenerator); + if(method.getInputModel() != null) { + Model model = method.getInputModel(); + if(model != null){ + if(!generatedClasses.contains(model.getName())) { + List imports = new ArrayList(); + imports.addAll(this.config.getDefaultModelImports()); + for(ModelField param : model.getFields()){ + for(String importDef : param.getFieldDefinition(this.getDataTypeMappingProvider()).getImportDefinitions()){ + if(!imports.contains(importDef)){ + imports.add(importDef); + } + } + } + StringTemplate template = templateGroup.getInstanceOf(MODEL_OBJECT_TEMPLATE); + + template.setAttribute("fields", model.getFields()); + template.setAttribute("imports", imports); + template.setAttribute("extends", config.getModelBaseClass()); + template.setAttribute("annotationPackageName", languageConfig.getAnnotationPackageName()); + template.setAttribute("className", model.getGenratedClassName()); + template.setAttribute(PACKAGE_NAME, config.getModelPackageName()); + File aFile = new File(languageConfig.getModelClassLocation()+model.getGenratedClassName()+languageConfig.getClassFileExtension()); + writeFile(aFile, template.toString(), "Input model class"); + generatedClasses.add(model.getName()); + } + } + } + } + } + } + } + } + } + + /** + * Generates an Enum class for method params that have an allowed values list. + * @param resources + * @param templateGroup + */ + private void generateEnumForAllowedValues(List resources, StringTemplateGroup templateGroup) { + List generatedEnums = new ArrayList(); + StringTemplate template; + String valuePrefix, valueSuffix = ""; + String enumName; + for(Resource resource: resources) { + if(resource.getEndPoints() != null) { + for(Endpoint endpoint : resource.getEndPoints()){ + if(endpoint.getOperations() != null) { + for(EndpointOperation operation : endpoint.getOperations()){ + //ResourceMethod method = operation.generateMethod(endpoint, resource, config); + if(operation.getParameters() != null){ + for(ModelField operationParam : operation.getParameters()){ + //skipping the case where there is just one item - TODO process case of allowableValue like '0 to 1000' + if(operationParam.getAllowableValues() != null && operationParam.getAllowableValues().size() > 1) { + if(!generatedEnums.contains(operationParam.getName())){ + //generate enum + template = templateGroup.getInstanceOf(ENUM_OBJECT_TEMPLATE); + List imports = new ArrayList(); + imports.addAll(this.config.getDefaultModelImports()); + enumName = this.getNameGenerator().getEnumName(operationParam.getName()); + template.setAttribute("className", enumName); + template.setAttribute("description", operationParam.getDescription()); + template.setAttribute("enumValueType", this.getDataTypeMappingProvider().getObjectType(operationParam.getDataType(), true)); + for (String allowableValue : operationParam.getAllowableValues()) { + if(operationParam.getDataType().equalsIgnoreCase("string")){ + valuePrefix = valueSuffix = "\""; + } + else{ + valuePrefix = valueSuffix = ""; + }; + template.setAttribute("values.{name,value}", + this.getNameGenerator().applyClassNamingPolicy(allowableValue.replaceAll("-","_")), + this.getNameGenerator().applyMethodNamingPolicy(valuePrefix.concat(allowableValue).concat(valueSuffix))); + } + template.setAttribute(PACKAGE_NAME, config.getModelPackageName()); + File aFile = new File(languageConfig.getModelClassLocation() + enumName + languageConfig.getClassFileExtension()); + writeFile(aFile, template.toString(), "Enum class"); + generatedEnums.add(operationParam.getName()); + } + } + } + } + } + } + } + } + + } + } + + /** + * Generates one API class for each resource and each end point in the resource is translated as method. + * @param resources + * @param templateGroup + */ + private void generateAPIClasses(List resources, StringTemplateGroup templateGroup) { + + for(Resource resource : resources) { + List methods = new ArrayList(); + List imports = new ArrayList(); + imports.addAll(this.config.getDefaultServiceImports()); + 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); + } + } + 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"); + } + } + + /** + * Creates a wrapper model class that contains all model classes as list of objects. + * This class is used for storing test data + */ + private void generateWrapperClassForTestData(List generatedClassNames, StringTemplateGroup templateGroup) { + Model model = new Model(); + model.setName("TestData"); + model.setDescription("Class used to store all the test data. This should not be used for any development"); + List modelFields = new ArrayList(); + model.setFields(modelFields); + for(String className : generatedClassNames){ + ModelField aParam = new ModelField(); + aParam.setName(this.getNameGenerator().applyMethodNamingPolicy(className)+"List"); + aParam.setParamType(this.getDataTypeMappingProvider().getListReturnTypeSignature(className)); + modelFields.add(aParam); + } + + //add missing class from models + ModelField aParam = new ModelField(); + aParam.setName("StringValueList"); + aParam.setParamType(this.getDataTypeMappingProvider().getListReturnTypeSignature("StringValue")); + modelFields.add(aParam); + + List imports = new ArrayList(); + imports.addAll(this.config.getDefaultModelImports()); + imports.addAll(this.getDataTypeMappingProvider().getListImportPackages()); + for(ModelField param : model.getFields()){ + for(String importDef : param.getFieldDefinition(this.getDataTypeMappingProvider()).getImportDefinitions()){ + if(!imports.contains(importDef)){ + imports.add(importDef); + } + } + } + StringTemplate template = templateGroup.getInstanceOf(MODEL_OBJECT_TEMPLATE); + template.setAttribute("fields", model.getFields()); + template.setAttribute("imports", imports); + template.setAttribute("annotationPackageName", languageConfig.getAnnotationPackageName()); + template.setAttribute("extends", config.getModelBaseClass()); + template.setAttribute(PACKAGE_NAME, config.getModelPackageName()); + template.setAttribute("className", model.getGenratedClassName()); + File aFile = new File(languageConfig.getModelClassLocation()+model.getGenratedClassName()+languageConfig.getClassFileExtension()); + writeFile(aFile, template.toString(), "Wrapper class for test data file"); + } + + private void writeFile(File aFile, String content, String classType){ + try{ + FileWriter aWriter = new FileWriter(aFile); + BufferedWriter bufWriter = new BufferedWriter(aWriter); + bufWriter.write(content); + bufWriter.close(); + }catch(IOException ioe){ + throw new CodeGenerationException("Error generating " + classType + " : " + ioe.getMessage()); + } + } + + public ApiConfiguration getConfig() { + return config; + } + + public void setApiConfig(ApiConfiguration config) { + this.config = config; + } + + public LanguageConfiguration getLanguageConfig() { + return languageConfig; + } + + public void setLanguageConfig(LanguageConfiguration config) { + this.languageConfig = config; + } + + public void setDataTypeMappingProvider(DataTypeMappingProvider dataTypeMappingProvider) { + this.dataTypeMappingProvider = dataTypeMappingProvider; + } + + public void setCodeGenRulesProvider(RulesProvider codeGenRulesProvider) { + this.codeGenRulesProvider = codeGenRulesProvider; + } + + public void setNameGenerator(NamingPolicyProvider nameGenerator) { + this.nameGenerator = nameGenerator; + } + + public DataTypeMappingProvider getDataTypeMappingProvider() { + return dataTypeMappingProvider; + } + + public RulesProvider getCodeGenRulesProvider() { + return codeGenRulesProvider; + } + + public NamingPolicyProvider getNameGenerator() { + return nameGenerator; + } +} diff --git a/src/main/java/com/wordnik/swagger/codegen/FieldDefinition.java b/src/main/java/com/wordnik/swagger/codegen/FieldDefinition.java new file mode 100644 index 00000000000..ea947f01200 --- /dev/null +++ b/src/main/java/com/wordnik/swagger/codegen/FieldDefinition.java @@ -0,0 +1,49 @@ +package com.wordnik.swagger.codegen; + +import java.util.ArrayList; +import java.util.List; + +public class FieldDefinition { + + private String returnType; + + private String name; + + private String initialization; + + private List importDefinitions = new ArrayList(); + + public String getReturnType() { + return returnType; + } + + public void setReturnType(String returnType) { + this.returnType = returnType; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getInitialization() { + return initialization; + } + + public void setInitialization(String initialization) { + this.initialization = initialization; + } + + public List getImportDefinitions() { + return importDefinitions; + } + + public String getNameForMethod() { + return name.substring(0,1).toUpperCase() + name.substring(1); + } + + +} diff --git a/src/main/java/com/wordnik/swagger/codegen/MethodArgument.java b/src/main/java/com/wordnik/swagger/codegen/MethodArgument.java new file mode 100644 index 00000000000..86a8e3aa0d7 --- /dev/null +++ b/src/main/java/com/wordnik/swagger/codegen/MethodArgument.java @@ -0,0 +1,68 @@ +package com.wordnik.swagger.codegen; + +import com.wordnik.swagger.codegen.config.NamingPolicyProvider; + +public class MethodArgument { + + public static String ARGUMENT_STRING = "String"; + public static String ARGUMENT_INTEGER = "int"; + public static String ARGUMENT_OBJECT = "Object"; + + private String name; + + private String description; + + private String dataType; + + private String allowedValues; + + private String inputModelClassArgument; + private String methodNameFromModelClass; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getDataType() { + return dataType; + } + + public void setDataType(String dataType) { + this.dataType = dataType; + } + + public String getAllowedValues() { + return allowedValues; + } + + public void setAllowedValues(String allowedValues) { + this.allowedValues = allowedValues; + } + + public String getInputModelClassArgument() { + return inputModelClassArgument; + } + + public void setInputModelClassArgument(String inputModelClass, NamingPolicyProvider nameGenerator) { + this.inputModelClassArgument = nameGenerator.applyMethodNamingPolicy(inputModelClass); + if(name != null) { + methodNameFromModelClass = nameGenerator.createGetterMethodName(inputModelClassArgument, name); + } + } + + public String getMethodNameFromModelClass() { + return methodNameFromModelClass; + } +} diff --git a/src/main/java/com/wordnik/swagger/codegen/ResourceMethod.java b/src/main/java/com/wordnik/swagger/codegen/ResourceMethod.java new file mode 100644 index 00000000000..1feceabbb16 --- /dev/null +++ b/src/main/java/com/wordnik/swagger/codegen/ResourceMethod.java @@ -0,0 +1,177 @@ +package com.wordnik.swagger.codegen; + +import com.wordnik.swagger.codegen.resource.Model; + +import java.util.List; + +public class ResourceMethod { + + private String description; + + private List arguments; + + private List queryParameters; + + private List pathParameters; + + 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; + + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public List getArguments() { + return arguments; + } + + public void setArguments(List arguments) { + this.arguments = arguments; + } + + public List getQueryParameters() { + return queryParameters; + } + + public void setQueryParameters(List queryParameters) { + this.queryParameters = queryParameters; + } + + public List getPathParameters() { + return pathParameters; + } + + public void setPathParameters(List pathParameters) { + this.pathParameters = pathParameters; + } + + public String getReturnValue() { + return returnValue; + } + + public void setReturnValue(String returnValue) { + this.returnValue = returnValue; + } + + public String getReturnClassName() { + return returnClassName; + } + + public void setReturnClassName(String returnClassName) { + this.returnClassName = returnClassName; + } + + public String getExceptionDescription() { + return exceptionDescription; + } + + public void setExceptionDescription(String exceptionDescription) { + this.exceptionDescription = exceptionDescription; + } + + public List getArgumentDefinitions() { + return argumentDefinitions; + } + + public void setArgumentDefinitions(List argumentDefinitions) { + this.argumentDefinitions = argumentDefinitions; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public boolean isAuthToken() { + return authToken; + } + + public void setAuthToken(boolean authToken) { + this.authToken = authToken; + } + + public String getResourcePath() { + return resourcePath; + } + + public void setResourcePath(String resourcePath) { + this.resourcePath = resourcePath; + } + + public String getMethodType() { + return methodType; + } + + public void setMethodType(String methodType) { + this.methodType = methodType; + } + + public boolean isPostObject() { + return postObject; + } + + public void setPostObject(boolean postObject) { + this.postObject = postObject; + } + + public boolean isResponseVoid() { + return (this.getReturnClassName().equalsIgnoreCase("void")); + } + + public Model getInputModel() { + return inputModel; + } + + public void setInputModel(Model inputModel) { + this.inputModel = inputModel; + } + + public List getArgumentNames() { + return argumentNames; + } + + public void setArgumentNames(List argumentNames) { + this.argumentNames = argumentNames; + } + + public boolean getHasArguments() { + if(this.getArgumentNames() != null && this.getArgumentNames().size() > 0){ + return true; + } + return false; + } + + public boolean isReturnValueList() { + if(this.getReturnValue().startsWith("List")){ + return true; + } + return false; + } +} diff --git a/src/main/java/com/wordnik/swagger/codegen/api/SwaggerResourceDocReader.java b/src/main/java/com/wordnik/swagger/codegen/api/SwaggerResourceDocReader.java new file mode 100644 index 00000000000..776cac05780 --- /dev/null +++ b/src/main/java/com/wordnik/swagger/codegen/api/SwaggerResourceDocReader.java @@ -0,0 +1,156 @@ +package com.wordnik.swagger.codegen.api; + +import com.sun.jersey.api.client.Client; +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.client.WebResource; +import com.wordnik.swagger.codegen.config.DataTypeMappingProvider; +import com.wordnik.swagger.codegen.resource.Endpoint; +import com.wordnik.swagger.codegen.resource.Resource; +import com.wordnik.swagger.codegen.config.ApiConfiguration; +import com.wordnik.swagger.codegen.config.NamingPolicyProvider; +import com.wordnik.swagger.exception.CodeGenerationException; +import org.codehaus.jackson.map.DeserializationConfig; +import org.codehaus.jackson.map.ObjectMapper; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * User: deepakmichael + * Date: 27/07/11 + * Time: 9:32 PM + */ +public class SwaggerResourceDocReader { + + private static String HEADER_NAME_API_VERSION = "Wordnik-Api-Version"; + + private String baseUrl; + private String apiKey; + private String apiListResource; + private ApiConfiguration apiConfiguration; + private final DataTypeMappingProvider dataTypeMappingProvider; + private final NamingPolicyProvider nameGenerator; + + public SwaggerResourceDocReader(ApiConfiguration apiConfiguration, DataTypeMappingProvider dataTypeMappingProvider, NamingPolicyProvider nameGenerator) { + this.apiConfiguration = apiConfiguration; + this.dataTypeMappingProvider = dataTypeMappingProvider; + this.nameGenerator = nameGenerator; + readApiConfig(); + + } + + public void readApiConfig() { + baseUrl = apiConfiguration.getApiUrl(); + apiKey = apiConfiguration.getApiKey(); + apiListResource = apiConfiguration.getApiListResource(); + } + + /** + * Reads the documentation of the resources and constructs the resource object that can be used + * for generating the driver related classes. The resource list string should be "," separated + */ + public List readResourceDocumentation() { + + List resourceDocs = new ArrayList(); + Client apiClient = Client.create(); + + String resourceList = retrieveResourceList(apiClient); + + //valid for input + if (baseUrl == null || resourceList == null || + baseUrl.trim().length() == 0 || + resourceList.trim().length() == 0) { + throw new CodeGenerationException("Base URL or Resource list input is null"); + } + + + //create list of resource URL + String[] resources = resourceList.split(","); + List resourceURLs = new ArrayList(); + for (String resource : resources) { + resource = trimResourceName(resource); + if (!resource.equals(trimResourceName( apiListResource ))) { + if(!resource.endsWith(".json")){ + resource = resource.concat(".json"); + } + resourceURLs.add(baseUrl + resource); + } + } + + //make connection to resource and get the documentation + for (String resourceURL : resourceURLs) { + WebResource aResource = apiClient.resource(resourceURL); + aResource.header("api_key", apiKey); + ClientResponse clientResponse = aResource.header("api_key", apiKey).get(ClientResponse.class); + String version = clientResponse.getHeaders().get(HEADER_NAME_API_VERSION).get(0);//TODO - check if this is required + String response = clientResponse.getEntity(String.class); + try { + ObjectMapper mapper = new ObjectMapper(); + mapper.getDeserializationConfig().set(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false); + Resource aResourceDoc = deserializeResource(response, mapper); + aResourceDoc.setApiVersion(version); + resourceDocs.add(aResourceDoc); + } catch (IOException ioe) { + ioe.printStackTrace(); + throw new CodeGenerationException("Error in coverting resource json documentation to java object"); + } + } + return resourceDocs; + + } + + private String trimResourceName(String resource) { + if(resource.startsWith("/")){ + resource = resource.substring(1,resource.length()); + } + return resource; + } + + private String retrieveResourceList(Client apiClient) { + String resourceCsv = ""; + Resource resourceApi; + String apiResourceUrl = null; + if(apiListResource == null){ + throw new CodeGenerationException("apiListingUrl needs to be defined in api configuration object"); + } + if(!apiListResource.endsWith(".json")){ + apiResourceUrl = trimResourceName( apiListResource.concat(".json") ); + } + + apiResourceUrl = baseUrl.concat(apiResourceUrl); + + WebResource aResource = apiClient.resource(apiResourceUrl); + aResource.header("api_key", apiKey); + ClientResponse clientResponse = aResource.header("api_key", apiKey).get(ClientResponse.class); + String response = clientResponse.getEntity(String.class); + try { + ObjectMapper mapper = new ObjectMapper(); + mapper.getDeserializationConfig().set(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false); + resourceApi = deserializeResource(response, mapper); + + for(Endpoint api: resourceApi.getEndPoints()){ + resourceCsv += (api.getPath() + ","); + } + } + catch (IOException ex) { + throw new CodeGenerationException("Error in coverting resource listing json documentation to java object"); + + } + return resourceCsv; + } + + /** + * Deserializes the response and returns a Response object + * @param response + * @param mapper + * @return + * @throws IOException + */ + private Resource deserializeResource(String response, ObjectMapper mapper) throws IOException { + Resource resource = mapper.readValue(response, Resource.class); + resource.generateModelsFromWrapper(nameGenerator); + return resource; + } + +} diff --git a/src/main/java/com/wordnik/swagger/codegen/config/ApiConfiguration.java b/src/main/java/com/wordnik/swagger/codegen/config/ApiConfiguration.java new file mode 100644 index 00000000000..276f05fbaac --- /dev/null +++ b/src/main/java/com/wordnik/swagger/codegen/config/ApiConfiguration.java @@ -0,0 +1,131 @@ +package com.wordnik.swagger.codegen.config; + +import com.wordnik.swagger.exception.CodeGenerationException; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * User: ramesh + * Date: 5/31/11 + * Time: 7:04 AM + */ +public class ApiConfiguration { + + private Map baseClassNames = new HashMap(); + + private String defaultServiceBaseClass = "Object"; + + private String modelBaseClass = "Object"; + /** + * Default model imports that we need to include in all service classes. This is needed because some times, + * we may need to write custom classes and those classes will not be known to code generation. To import those + * classes in service classes we use this property + */ + private List defaultModelImports; + /** + * Default service imports that we need to include in all service classes. This is needed because some times, + * we may need to write custom classes ans those classes will not be known to code generation. To import those + * classes in service classes we use this property + */ + private List defaultServiceImports; + private String modelPackageName; + private String apiPackageName; + + private String apiUrl; + private String apiKey; + private String apiListResource; + + public ApiConfiguration() { + + } + + public void setServiceBaseClass(String defaultServiceBaseClass) { + this.defaultServiceBaseClass = defaultServiceBaseClass; + } + + public void setServiceBaseClass(String serviceName, String className) { + if(serviceName == null || serviceName.length() == 0){ + throw new CodeGenerationException("Error setting base class for service: service name was not provided"); + } + + if(className == null || className.length() == 0) { + throw new CodeGenerationException("Error settting base class for service: class name was not provided"); + } + + baseClassNames.put(serviceName, className); + } + + public String getServiceBaseClass(String serviceName) { + if(baseClassNames.containsKey(serviceName)){ + return baseClassNames.get(serviceName); + } + return defaultServiceBaseClass; + } + + public String getModelBaseClass() { + return modelBaseClass; + } + + public void setModelBaseClass(String modelBaseClass) { + this.modelBaseClass = modelBaseClass; + } + + + public List getDefaultModelImports() { + return defaultModelImports; + } + + public void setDefaultModelImports(List defaultModelImports) { + this.defaultModelImports = defaultModelImports; + } + + public List getDefaultServiceImports() { + return defaultServiceImports; + } + + public void setDefaultServiceImports(List defaultServiceImports) { + this.defaultServiceImports = defaultServiceImports; + } + + public String getModelPackageName() { + return modelPackageName; + } + + public void setModelPackageName(String modelPackageName) { + this.modelPackageName = modelPackageName; + } + + public String getApiPackageName() { + return apiPackageName; + } + + public void setApiPackageName(String apiPackageName) { + this.apiPackageName = apiPackageName; + } + + public String getApiUrl() { + return apiUrl; + } + + public void setApiUrl(String apiUrl) { + this.apiUrl = apiUrl; + } + + public String getApiKey() { + return apiKey; + } + + public void setApiKey(String apiKey) { + this.apiKey = apiKey; + } + + public String getApiListResource() { + return apiListResource; + } + + public void setApiListResource(String apiListResource) { + this.apiListResource = apiListResource; + } +} diff --git a/src/main/java/com/wordnik/swagger/codegen/config/DataTypeMappingProvider.java b/src/main/java/com/wordnik/swagger/codegen/config/DataTypeMappingProvider.java new file mode 100644 index 00000000000..ab88b8a1a95 --- /dev/null +++ b/src/main/java/com/wordnik/swagger/codegen/config/DataTypeMappingProvider.java @@ -0,0 +1,187 @@ +package com.wordnik.swagger.codegen.config; + +import java.util.List; + +/** + * Implementations of this class is responsible for generating mapping between rest data types and language + * specific data type + * + * User: ramesh + * Date: 5/27/11 + * Time: 7:39 AM + */ +public interface DataTypeMappingProvider { + + /** + * Checks nature of data type. + * + * This is needed in generating return values, input and model class generations. + * + * Example: in java String, Integer, Boolean are considered as primitive + * types + * @param type + * @return + */ + public boolean isPrimitiveType(String type); + + /** + * provide the sttring that needs to be used when defining methods that returns no values + * + * Example: in java this value will be void + * @return + */ + public String getReturnTypeForVoidMethods(); + + /** + * Signature that should be used when returning list of given object type. + * + * Example: in java this output will look as List for methods that returns a list of user objects + * @param typeClass of class that list object contains. + * @return + */ + public String getListReturnTypeSignature(String typeClass); + + /** + * Signature that should be used when returning map of given object type. + * + * Example: in java this output will look as Map for methods that returns maps + * @param typeClass of class that list object contains. + * @return + */ + public String getMapReturnTypeSignature(String typeClass); + + /** + * Signature that should be used when returning set of given object type. + * + * Example: in java this output will look as Set for methods that returns a set of user objects + * @param typeClass of class that the set object contains. + * @return + */ + public String getSetReturnTypeSignature(String typeClass); + + /** + * Initialization need for list objects. Example. If it is java list the initialization will look as + * + * + * new ArrayList() + * + * + * @param typeClass + * @return + */ + public String generateListInitialization(String typeClass); + + /** + * Initialization need for map objects. Example. If it is java map the initialization will look as + * + * + * new HashMap() + * + * + * @param typeClass + * @return + */ + public String generateMapInitialization(String typeClass); + + /** + * Initialization need for set objects. Example. If it is java set the initialization will look as + * + * + * new HashSet() + * + * + * @param typeClass + * @return + */ + public String generateSetInitialization(String typeClass); + + /** + * Gets list of imports that needs to be included when used objects of type List. + * + * Example: in java while using lists we use an interface of List and implementation of + * ArrayList. SO the output will as follows: + * + * List imports = new ArrayList(); + imports.add("java.util.List"); + imports.add("java.util.ArrayList"); + * + * @return + */ + public List getListImportPackages(); + + /** + * Gets list of imports that needs to be included when used objects of type Map. + * + * Example: in java while using maps we use an interface of Map and implementation of + * HashMap. SO the output will as follows: + * + * List imports = new ArrayList(); + imports.add("java.util.Map"); + imports.add("java.util.HashMap"); + * + * @return + */ + public List getMapImportPackages(); + + /** + * Gets list of imports that needs to be included when used objects of type Set. + * + * Example: in java while using sets we use an interface of Set and implementation of + * HashSet. SO the output will as follows: + * + * List imports = new ArrayList(); + imports.add("java.util.Set"); + imports.add("java.util.HashSet"); + * + * @return + */ + public List getSetImportPackages(); + + /** + * Gets list of imports that needs to be included when used objects of type Date. + * + * Example: in java while using Data we use . So the output will as follows: + * + * List imports = new ArrayList(); + imports.add("java.util.Date"); + * + * @return + */ + public List getDateImports(); + + /** + * Object type definition for a given input + * + * @param type + * @param primitiveObject + * @return + */ + public String getObjectType(String type, boolean primitiveObject); + + /** + * Gets the value of return type converted from web service response documentation. + * + * Example: If the resource documentation ays return type as List[User] the equivalent translation for java will be + * + * List + * + * If the input is Map[int, String] the equivalent java translation will be Map + * @param type + * @return + */ + public String getReturnValueType(String type); + + /** + * Gets the class of return values from web service response documentation. If the service returns list the class + * indicates type of object in the list + * + * Example: If the resource documentation ays return type as List[User] the equivalent translation for java will be + * + * User + * + * If the input is Map[int] the equivalent java translation will be Int + * @param type + * @return + */ + public String getReturnClassType(String type); +} diff --git a/src/main/java/com/wordnik/swagger/codegen/config/LanguageConfiguration.java b/src/main/java/com/wordnik/swagger/codegen/config/LanguageConfiguration.java new file mode 100644 index 00000000000..1632453315d --- /dev/null +++ b/src/main/java/com/wordnik/swagger/codegen/config/LanguageConfiguration.java @@ -0,0 +1,78 @@ +package com.wordnik.swagger.codegen.config; + +import com.wordnik.swagger.exception.CodeGenerationException; + +/** + * User: deepakmichael + * Date: 23/07/11 + * Time: 8:01 AM + */ +public class LanguageConfiguration { + + private String classFileExtension; + + private String templateLocation; + + private String modelClassLocation; + + private String resourceClassLocation; + + private String exceptionPackageName; + + private String annotationPackageName; + + public String getClassFileExtension() { + return classFileExtension; + } + + public void setClassFileExtension(String classFileExtension) { + this.classFileExtension = classFileExtension; + } + + public String getTemplateLocation() { + return templateLocation; + } + + public void setTemplateLocation(String templateLocation) { + this.templateLocation = templateLocation; + } + + public void setOutputDirectory(String outputDirectory) { + + if(outputDirectory == null || outputDirectory.length() == 0){ + throw new CodeGenerationException("Error creating output path : Output path was null "); + } + outputDirectory = outputDirectory.endsWith("/") ? outputDirectory.substring(0, outputDirectory.lastIndexOf("/")) : outputDirectory; + + + this.modelClassLocation = outputDirectory + "/model/"; + this.resourceClassLocation = outputDirectory + "/api/"; + } + + public String getModelClassLocation() { + return modelClassLocation; + } + + public String getResourceClassLocation() { + return resourceClassLocation; + } + + public String getExceptionPackageName() { + return exceptionPackageName; + } + + public void setExceptionPackageName(String exceptionPackageName) { + this.exceptionPackageName = exceptionPackageName; + } + + public String getAnnotationPackageName() { + return annotationPackageName; + } + + public void setAnnotationPackageName(String annotationPackageName) { + this.annotationPackageName = annotationPackageName; + } + + + +} diff --git a/src/main/java/com/wordnik/swagger/codegen/config/NamingPolicyProvider.java b/src/main/java/com/wordnik/swagger/codegen/config/NamingPolicyProvider.java new file mode 100644 index 00000000000..590f57d2683 --- /dev/null +++ b/src/main/java/com/wordnik/swagger/codegen/config/NamingPolicyProvider.java @@ -0,0 +1,105 @@ +package com.wordnik.swagger.codegen.config; + +/** + * Implementor of this class is responsible for generating the names for service classes and methods in + * each of those service classes + * + * User: ramesh + * Date: 5/27/11 + * Time: 7:36 AM + */ +public interface NamingPolicyProvider { + + /** + * Gets name of the version checker class. We need not provide extension for the class as that will be read + * through a class extention configuration value. + * + * Example: In java this is VersionChecker. + * + * @return + */ + public String getVersionCheckerClassName(); + + + /** + * Convert input string into class name format. + * + * Example: in java this will be init caps. + * + * @param input + * @return + */ + public String applyClassNamingPolicy(String input); + + /** + * Transform the input string into method naming convention format. + * + * Example: In java, the will be Camel case + * + * @param input + * @return + */ + public String applyMethodNamingPolicy(String input); + + /** + * Generates the name of service based on resource path. + * + * Example: for a resource path of http://beta.wordnik.com/v4/word.json the service name can be WordAPI + * + * @param resourcePath + * @return + */ + public String getServiceName(String resourcePath); + + + /** + * 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); + + + /** + * Generate of the input object using the resource path name. + * + * When the number of arguments for a method increases beyond certain limit, we want to capture all the arguments + * as a single input objects so that it is easy for client to understand the API. + * + * Example: get examples API on words resource takes inputs as : word, limit, include duplicates, content provider, + * use canonical, skip, limit. Instead of having all these as individual arguments create an input object with name + * WordsExampleInput and have above arguments as properties fo the object. + * + * @param serviceName + * @param resourcePath + * @return + */ + public String getInputObjectName(String serviceName, String resourcePath); + + /** + * 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); + + /** + * 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); +} diff --git a/src/main/java/com/wordnik/swagger/codegen/config/RulesProvider.java b/src/main/java/com/wordnik/swagger/codegen/config/RulesProvider.java new file mode 100644 index 00000000000..48f9756fc4f --- /dev/null +++ b/src/main/java/com/wordnik/swagger/codegen/config/RulesProvider.java @@ -0,0 +1,23 @@ +package com.wordnik.swagger.codegen.config; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Maintains the overriding rules that we should use while generating the code. + * + * Example; If we need to ignore any REST methods or if we need special service extention classes they can be + * supplied through this configuration + * + * User: ramesh + * Date: 4/26/11 + * Time: 8:01 AM + */ +public interface RulesProvider { + + public boolean isMethodIgnored(String serviceName, String methodName); + + public boolean isModelIgnored(String modelName); +} diff --git a/src/main/java/com/wordnik/swagger/codegen/config/common/CamelCaseNamingPolicyProvider.java b/src/main/java/com/wordnik/swagger/codegen/config/common/CamelCaseNamingPolicyProvider.java new file mode 100644 index 00000000000..e182d836808 --- /dev/null +++ b/src/main/java/com/wordnik/swagger/codegen/config/common/CamelCaseNamingPolicyProvider.java @@ -0,0 +1,133 @@ +package com.wordnik.swagger.codegen.config.common; + +import com.wordnik.swagger.codegen.resource.Model; +import com.wordnik.swagger.codegen.config.NamingPolicyProvider; +import com.wordnik.swagger.exception.CodeGenerationException; + +/** + * User: ramesh + * Date: 5/31/11 + * Time: 7:03 AM + */ +public class CamelCaseNamingPolicyProvider implements NamingPolicyProvider { + + /** + * gets the name of class that is responsible for tracking current library version + * @return + */ + public String getVersionCheckerClassName() { + return "VersionChecker"; + } + + /** + * Converts the first character of the input into string. + * 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 input.substring(0,1).toUpperCase() + input.substring(1); + }else{ + throw new CodeGenerationException("Error converting input to first letter caps becuase of null input"); + } + } + + /** + * Converts the first character of the input into string. + * Example: If the input is word, the return value will be Word + * @param input + * @return + */ + public String applyMethodNamingPolicy(String input) { + if(input != null && input.length() > 0) { + return input.substring(0,1).toLowerCase() + input.substring(1); + }else{ + throw new CodeGenerationException("Error converting input to first letter to lower because of null input"); + } + } + + 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 suggestedName; + } + + + public String getInputObjectName(String serviceName, String resourcePath) { + + 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) + Model.INPUT_OBJECT_SUFFIX; + } + } + } + } + return inputobjectName; + } + + /** + * 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/java/JavaCodeGenRulesProvider.java b/src/main/java/com/wordnik/swagger/codegen/config/java/JavaCodeGenRulesProvider.java new file mode 100644 index 00000000000..a70681bb5f8 --- /dev/null +++ b/src/main/java/com/wordnik/swagger/codegen/config/java/JavaCodeGenRulesProvider.java @@ -0,0 +1,33 @@ +package com.wordnik.swagger.codegen.config.java; + +import com.wordnik.swagger.codegen.config.RulesProvider; + +import java.util.ArrayList; +import java.util.List; + +/** + * User: ramesh + * Date: 5/31/11 + * Time: 7:04 AM + */ +public class JavaCodeGenRulesProvider implements RulesProvider { + + private List ignoreMethods = new ArrayList(); + private List ignoreModels = new ArrayList(); + + public JavaCodeGenRulesProvider() { + ignoreMethods.add("WordAPI.getWordFrequency"); + ignoreMethods.add("WordAPI.getAudio"); + ignoreMethods.add("WordAPI.getWordStats"); + ignoreModels.add("wordStats"); + } + + public boolean isMethodIgnored(String serviceName, String methodName){ + return (ignoreMethods.contains(serviceName+"."+methodName)); + } + + public boolean isModelIgnored(String modelName) { + return ignoreModels.contains(modelName); + } + +} diff --git a/src/main/java/com/wordnik/swagger/codegen/config/java/JavaDataTypeMappingProvider.java b/src/main/java/com/wordnik/swagger/codegen/config/java/JavaDataTypeMappingProvider.java new file mode 100644 index 00000000000..769a8a01200 --- /dev/null +++ b/src/main/java/com/wordnik/swagger/codegen/config/java/JavaDataTypeMappingProvider.java @@ -0,0 +1,190 @@ +package com.wordnik.swagger.codegen.config.java; + +import com.wordnik.swagger.codegen.config.DataTypeMappingProvider; +import com.wordnik.swagger.codegen.config.NamingPolicyProvider; +import com.wordnik.swagger.codegen.config.common.CamelCaseNamingPolicyProvider; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * User: ramesh + * Date: 5/31/11 + * Time: 7:03 AM + */ +public class JavaDataTypeMappingProvider implements DataTypeMappingProvider { + + public static Map primitiveValueMap = new HashMap(); + static{ + primitiveValueMap.put("string", "String"); + primitiveValueMap.put("String", "String"); + primitiveValueMap.put("int", "int"); + primitiveValueMap.put("integer", "int"); + primitiveValueMap.put("Integer", "int"); + primitiveValueMap.put("boolean", "boolean"); + primitiveValueMap.put("Boolean", "boolean"); + primitiveValueMap.put("long", "long"); + primitiveValueMap.put("Long", "long"); + primitiveValueMap.put("float", "float"); + primitiveValueMap.put("Float", "float"); + primitiveValueMap.put("Date", "Date"); + primitiveValueMap.put("date", "Date"); + } + + public static Map primitiveObjectMap = new HashMap(); + static{ + primitiveObjectMap.put("string", "String"); + primitiveObjectMap.put("String", "String"); + primitiveObjectMap.put("int", "Integer"); + primitiveObjectMap.put("integer", "Integer"); + primitiveObjectMap.put("Integer", "Integer"); + primitiveObjectMap.put("boolean", "Boolean"); + primitiveObjectMap.put("Boolean", "Boolean"); + primitiveObjectMap.put("long", "Long"); + primitiveObjectMap.put("Long", "Long"); + primitiveObjectMap.put("float", "Float"); + primitiveObjectMap.put("Float", "Float"); + primitiveObjectMap.put("Date", "Date"); + primitiveObjectMap.put("date", "Date"); + } + + private NamingPolicyProvider nameGenerator = new CamelCaseNamingPolicyProvider(); + + public boolean isPrimitiveType(String type) { + if(type.equalsIgnoreCase("String") || type.equalsIgnoreCase("int") || type.equalsIgnoreCase("integer") || + type.equalsIgnoreCase("boolean") || type.equalsIgnoreCase("float")|| type.equalsIgnoreCase("long") ){ + return true; + } + return false; + } + + /** + * If the data type is primitive and it is expecting object structure then return primitive objects + * else return primitive types + * @param type + * @param primitiveObject -- indicates if the object is primitive or not + * @return + */ + public String getObjectType(String type, boolean primitiveObject) { + if(isPrimitiveType(type)){ + if(primitiveObject){ + return primitiveObjectMap.get(type); + }else{ + return primitiveValueMap.get(type); + } + }else{ + return nameGenerator.applyClassNamingPolicy(type); + } + } + + public String getListReturnTypeSignature(String typeClass) { + return "List<"+nameGenerator.applyClassNamingPolicy(typeClass)+">"; + } + + public String getReturnTypeForVoidMethods() { + return "void"; + } + + public String getMapReturnTypeSignature(String typeClass) { + return "Map<"+nameGenerator.applyClassNamingPolicy(typeClass)+">"; + } + + public String getSetReturnTypeSignature(String typeClass) { + return "Set<"+nameGenerator.applyClassNamingPolicy(typeClass)+">"; + } + + public String generateListInitialization(String typeClass) { + return " new ArrayList<"+nameGenerator.applyClassNamingPolicy(typeClass)+">()"; + } + + public String generateMapInitialization(String typeClass) { + return " new HashMap<"+nameGenerator.applyClassNamingPolicy(typeClass)+">()"; + } + + public String generateSetInitialization(String typeClass) { + return " new HashSet<"+nameGenerator.applyClassNamingPolicy(typeClass)+">()"; + } + + public List getListImportPackages() { + List imports = new ArrayList(); + imports.add("java.util.List"); + imports.add("java.util.ArrayList"); + return imports; + } + + public List getMapImportPackages() { + List imports = new ArrayList(); + imports.add("java.util.Map"); + imports.add("java.util.HashMap"); + return imports; + } + + public List getSetImportPackages() { + List imports = new ArrayList(); + imports.add("java.util.Set"); + imports.add("java.util.HashSet"); + return imports; } + + + public List getDateImports() { + List imports = new ArrayList(); + imports.add("java.util.Date"); + return imports; + } + + /** + * Gets the short name of the class the class. + * Input can be MAP, LIST or regular string. In case of map or list the class name will be class name + * that map or list is returning. + * @param type + * @return + */ + public String getReturnClassType(String type) { + String classShortName = ""; + if(type.startsWith("List[")){ + classShortName = type.substring(5, type.length()-1); + classShortName = getObjectType(classShortName, true); + }else if (type.startsWith("Map[")) { + classShortName = type.substring(4, type.length()-1); + classShortName = getObjectType(classShortName, true); + }else if (type.startsWith("Set[")) { + classShortName = type.substring(4, type.length()-1); + classShortName = getObjectType(classShortName, true); + }else if (type.equals("ok")) { + classShortName = "void"; + }else{ + classShortName = getObjectType(type, true); + } + return classShortName; + } + + /** + * Gets the class of the expected return value for a type string. Examples of type Strings are int, User, List[User] + * If the type string is a collection type like a map or list the string value returned would be the class + * that map or list is returning. + * + * @param type + * @return + */ + public String getReturnValueType(String type) { + if(type.equalsIgnoreCase("void")|| type.equalsIgnoreCase("ok")){ + return "void"; + } + String classShortName = ""; + if(type.startsWith("List[")){ + classShortName = type.substring(5, type.length()-1); + classShortName = "List<"+getObjectType(classShortName, true)+">"; + }else if (type.startsWith("Map[")) { + classShortName = type.substring(4, type.length()-1); + classShortName = "Map<"+getObjectType(classShortName, true) +">"; + }else if (type.startsWith("Set[")) { + classShortName = type.substring(4, type.length()-1); + classShortName = "Set<"+getObjectType(classShortName, true) +">"; + }else{ + classShortName = getObjectType(type, true); + } + return classShortName; + } +} diff --git a/src/main/java/com/wordnik/swagger/codegen/config/java/JavaLibCodeGen.java b/src/main/java/com/wordnik/swagger/codegen/config/java/JavaLibCodeGen.java new file mode 100644 index 00000000000..39f0717d1c3 --- /dev/null +++ b/src/main/java/com/wordnik/swagger/codegen/config/java/JavaLibCodeGen.java @@ -0,0 +1,80 @@ +package com.wordnik.swagger.codegen.config.java; + +import com.wordnik.swagger.codegen.DriverCodeGenerator; +import com.wordnik.swagger.codegen.config.ApiConfiguration; +import com.wordnik.swagger.codegen.config.LanguageConfiguration; +import com.wordnik.swagger.codegen.config.common.CamelCaseNamingPolicyProvider; +import com.wordnik.swagger.exception.CodeGenerationException; + +import java.util.ArrayList; +import java.util.List; + +/** + * User: ramesh + * Date: 6/16/11 + * Time: 1:31 PM + */ +public class JavaLibCodeGen extends DriverCodeGenerator { + + public static void main(String[] args) { + if(args.length < 1){ + throw new CodeGenerationException("Invalid number of arguments passed: No command line argument was passed to the program for output path"); + } + + String outputPath = args[0]; + JavaLibCodeGen codeGenerator = new JavaLibCodeGen(outputPath); + codeGenerator.generateCode(); + } + + public JavaLibCodeGen(String outputPath){ + + this.setApiConfig(initializeApiConfig()); + this.setLanguageConfig(initializeLangConfig(outputPath)); + + this.setDataTypeMappingProvider(new JavaDataTypeMappingProvider()); + this.setCodeGenRulesProvider(new JavaCodeGenRulesProvider()); + this.setNameGenerator(new CamelCaseNamingPolicyProvider()); + } + + private ApiConfiguration initializeApiConfig() { + ApiConfiguration apiConfiguration = new ApiConfiguration(); + apiConfiguration.setServiceBaseClass("WordAPI","AbstractWordAPI"); + //default base class for services if not specified for a service + apiConfiguration.setServiceBaseClass("WordnikAPI"); + apiConfiguration.setModelBaseClass("WordnikObject"); + + List defaultModelImports = new ArrayList(); + defaultModelImports.add("com.wordnik.swagger.common.WordListType"); + defaultModelImports.add("com.wordnik.swagger.common.StringValue"); + defaultModelImports.add("com.wordnik.swagger.common.Size"); + defaultModelImports.add("com.wordnik.swagger.common.WordnikObject"); + + List defaultServiceImports = new ArrayList(); + defaultServiceImports.add("com.wordnik.swagger.model.Long"); + defaultServiceImports.add("com.wordnik.swagger.common.*"); + defaultServiceImports.add("com.wordnik.swagger.common.ext.*"); + + apiConfiguration.setDefaultModelImports(defaultModelImports); + apiConfiguration.setDefaultServiceImports(defaultServiceImports); + + apiConfiguration.setModelPackageName("com.wordnik.swagger.model"); + apiConfiguration.setApiPackageName("com.wordnik.swagger.api"); + + apiConfiguration.setApiKey("myKey"); + apiConfiguration.setApiUrl("http://swagr.api.wordnik.com/v4/"); + apiConfiguration.setApiListResource("/list"); + + return apiConfiguration; + } + + private LanguageConfiguration initializeLangConfig(String outputPath) { + LanguageConfiguration javaConfiguration = new LanguageConfiguration(); + javaConfiguration.setClassFileExtension(".java"); + javaConfiguration.setOutputDirectory(outputPath); + javaConfiguration.setTemplateLocation("conf/java/templates"); + javaConfiguration.setExceptionPackageName("com.wordnik.swagger.exception"); + javaConfiguration.setAnnotationPackageName("com.wordnik.swagger.annotations"); + return javaConfiguration; + } + +} diff --git a/src/main/java/com/wordnik/swagger/codegen/resource/ApiModelDefn.java b/src/main/java/com/wordnik/swagger/codegen/resource/ApiModelDefn.java new file mode 100644 index 00000000000..ff8e2a53881 --- /dev/null +++ b/src/main/java/com/wordnik/swagger/codegen/resource/ApiModelDefn.java @@ -0,0 +1,53 @@ +package com.wordnik.swagger.codegen.resource; + +import com.wordnik.swagger.codegen.config.NamingPolicyProvider; +import org.codehaus.jackson.annotate.JsonProperty; + +/** + * User: deepakmichael + * Date: 19/07/11 + * Time: 1:21 AM + */ +public class ApiModelDefn { + + @JsonProperty("id") + private String id; + @JsonProperty("properties") + private ApiPropertyListWrapper properties; + @JsonProperty("description") + private String description; + + @JsonProperty("id") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + @JsonProperty("properties") + public ApiPropertyListWrapper getProperties() { + return properties; + } + + public void setProperties(ApiPropertyListWrapper properties) { + this.properties = properties; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Model toModel(String modelName, NamingPolicyProvider nameGenerator) { + Model model = new Model(); + model.setName(modelName); + model.setDescription(this.getDescription()); + model.setFields( this.getProperties().toFieldList( nameGenerator ) ); + return model; + } +} diff --git a/src/main/java/com/wordnik/swagger/codegen/resource/ApiModelListWrapper.java b/src/main/java/com/wordnik/swagger/codegen/resource/ApiModelListWrapper.java new file mode 100644 index 00000000000..82e2e334ccd --- /dev/null +++ b/src/main/java/com/wordnik/swagger/codegen/resource/ApiModelListWrapper.java @@ -0,0 +1,27 @@ +package com.wordnik.swagger.codegen.resource; + +import org.codehaus.jackson.annotate.JsonAnyGetter; +import org.codehaus.jackson.annotate.JsonAnySetter; +import org.codehaus.jackson.map.annotate.JsonSerialize; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) +public class ApiModelListWrapper implements Serializable +{ + + private Map modelList = new HashMap(); + + @JsonAnyGetter + public Map getModelList() { + return this.modelList; + } + + @JsonAnySetter + public void setModelList(String modelName, ApiModelDefn modelDefn) { + this.modelList.put(modelName, modelDefn); + } + +} diff --git a/src/main/java/com/wordnik/swagger/codegen/resource/ApiPropertyDefn.java b/src/main/java/com/wordnik/swagger/codegen/resource/ApiPropertyDefn.java new file mode 100644 index 00000000000..4ecb60a0f03 --- /dev/null +++ b/src/main/java/com/wordnik/swagger/codegen/resource/ApiPropertyDefn.java @@ -0,0 +1,176 @@ +package com.wordnik.swagger.codegen.resource; + +import org.codehaus.jackson.annotate.JsonAnyGetter; +import org.codehaus.jackson.annotate.JsonAnySetter; +import org.codehaus.jackson.annotate.JsonProperty; +import org.codehaus.jackson.annotate.JsonPropertyOrder; +import org.codehaus.jackson.map.annotate.JsonSerialize; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) +@JsonPropertyOrder({ + "id", + "default", + "items", + "description", + "name", + "enum", + "properties", + "required", + "notes", + "access", + "type" +}) +public class ApiPropertyDefn implements Serializable +{ + + @JsonProperty("id") + private String id; + @JsonProperty("default") + private String defaultValue; + @JsonProperty("items") + private ApiPropertyDefn items; + @JsonProperty("description") + private String description; + @JsonProperty("name") + private String name; + @JsonProperty("enum") + private List possibleValues = new ArrayList(); + @JsonProperty("properties") + private ApiPropertyListWrapper properties; + @JsonProperty("required") + private boolean required; + @JsonProperty("notes") + private String notes; + @JsonProperty("access") + private String access; + @JsonProperty("type") + private String type; + private Map additionalProperties = new HashMap(); + + @JsonProperty("id") + public String getId() { + return id; + } + + @JsonProperty("id") + public void setId(String id) { + this.id = id; + } + + @JsonProperty("default") + public String getDefaultValue() { + return defaultValue; + } + + @JsonProperty("default") + public void setDefault(String defaultvalue) { + this.defaultValue = defaultValue; + } + + @JsonProperty("items") + public ApiPropertyDefn getItems() { + return items; + } + + @JsonProperty("items") + public void setItems(ApiPropertyDefn items) { + this.items = items; + } + + @JsonProperty("description") + public String getDescription() { + return description; + } + + @JsonProperty("description") + public void setDescription(String description) { + this.description = description; + } + + @JsonProperty("name") + public String getName() { + return name; + } + + @JsonProperty("name") + public void setName(String name) { + this.name = name; + } + + @JsonProperty("enum") + public List getPossibleValues() { + return possibleValues; + } + + @JsonProperty("enum") + public void setEnum(List possibleValues) { + this.possibleValues = possibleValues; + } + + @JsonProperty("properties") + public ApiPropertyListWrapper getProperties() { + return properties; + } + + @JsonProperty("properties") + public void setProperties(ApiPropertyListWrapper properties) { + this.properties = properties; + } + + @JsonProperty("required") + public boolean isRequired() { + return required; + } + + @JsonProperty("required") + public void setRequired(boolean required) { + this.required = required; + } + + @JsonProperty("notes") + public String getNotes() { + return notes; + } + + @JsonProperty("notes") + public void setNotes(String notes) { + this.notes = notes; + } + + @JsonProperty("access") + public String getAccess() { + return access; + } + + @JsonProperty("access") + public void setAccess(String access) { + this.access = access; + } + + @JsonProperty("type") + public String getType() { + return type; + } + + @JsonProperty("type") + public void setType(String type) { + this.type = type; + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + @JsonAnySetter + public void setAdditionalProperties(String name, Object value) { + this.additionalProperties.put(name, value); + } + +} diff --git a/src/main/java/com/wordnik/swagger/codegen/resource/ApiPropertyListWrapper.java b/src/main/java/com/wordnik/swagger/codegen/resource/ApiPropertyListWrapper.java new file mode 100644 index 00000000000..e44affc8f76 --- /dev/null +++ b/src/main/java/com/wordnik/swagger/codegen/resource/ApiPropertyListWrapper.java @@ -0,0 +1,67 @@ +package com.wordnik.swagger.codegen.resource; + +import com.wordnik.swagger.codegen.config.NamingPolicyProvider; +import org.codehaus.jackson.annotate.JsonAnyGetter; +import org.codehaus.jackson.annotate.JsonAnySetter; +import org.codehaus.jackson.map.annotate.JsonSerialize; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) +public class ApiPropertyListWrapper implements Serializable +{ + + private Map propertyList = new HashMap(); + + @JsonAnyGetter + public Map getPropertyList() { + return this.propertyList; + } + + @JsonAnySetter + public void setPropertyList(String name, ApiPropertyDefn value) { + this.propertyList.put(name, value); + } + + public List toFieldList(NamingPolicyProvider nameGenerator) { + List fields = new ArrayList(); + ModelField field; + + String propertyName; + ApiPropertyDefn propertyDefn; + for(Map.Entry propertyDefnEntry : this.getPropertyList().entrySet()) { + propertyName = propertyDefnEntry.getKey(); + propertyDefn = propertyDefnEntry.getValue(); + + field = new ModelField(); + field.setName(propertyName); + //TODO - need to handle this via the nameGenerator which will do this in case the propertyName is a key word in the language + if(propertyName.equals("enum") || propertyName.equals("default")){ + field.setName(propertyName+"Value"); + } + field.setDescription(propertyDefn.getDescription()); + //field.setAllowableValues(propertyDefn.getPossibleValues()); //TODO + //field.setDataType(propertyDefn.getType()); //TODO - verify if this is needed for a model field - paramType is set + field.setParamType(propertyDefn.getType()); + if(propertyDefn.getType().equals("array")){ + String arrayItemType = propertyDefn.getItems().getType(); + if(propertyDefn.getItems().getAdditionalProperties().get("$ref") != null) { + arrayItemType = (String) propertyDefn.getItems().getAdditionalProperties().get("$ref"); + } + field.setParamType("List[" + nameGenerator.applyClassNamingPolicy(arrayItemType) + "]"); + } + field.setDefaultValue(propertyDefn.getDefaultValue()); + field.setInternalDescription(propertyDefn.getNotes()); + field.setParamAccess(propertyDefn.getAccess()); + field.setRequired(propertyDefn.isRequired()); + //field.setWrapperName(propertyDefn); + fields.add(field); + } + return fields; + } + +} diff --git a/src/main/java/com/wordnik/swagger/codegen/resource/Endpoint.java b/src/main/java/com/wordnik/swagger/codegen/resource/Endpoint.java new file mode 100644 index 00000000000..249ebf768dc --- /dev/null +++ b/src/main/java/com/wordnik/swagger/codegen/resource/Endpoint.java @@ -0,0 +1,122 @@ +package com.wordnik.swagger.codegen.resource; + +import com.wordnik.swagger.codegen.ResourceMethod; +import com.wordnik.swagger.codegen.config.DataTypeMappingProvider; +import com.wordnik.swagger.codegen.config.NamingPolicyProvider; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by IntelliJ IDEA. + * User: ramesh + * Date: 3/30/11 + * Time: 7:01 PM + * To change this template use File | Settings | File Templates. + */ +public class Endpoint { + + private String path; + + private String description; + + private List pathParameters; + + private List operations; + + private List methods; + + private List errorResponses; + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public List getPathParameters() { + return pathParameters; + } + + public void setPathParameters(List pathParameters) { + this.pathParameters = pathParameters; + } + + public List getOperations() { + return operations; + } + + public void setOperations(List operations) { + this.operations = operations; + setOperationResponses(); + } + + public List getErrorResponses() { + return errorResponses; + } + + public void setErrorResponses(List errorResponses) { + this.errorResponses = errorResponses; + setOperationResponses(); + } + + private void setOperationResponses() { + if(this.errorResponses != null && this.operations != null && this.operations.size() > 0 ){ + for(EndpointOperation operation: this.operations){ + if(operation.getResponse() != null & operation.getResponse().size() > 0){ + for(Response response : operation.getResponse()){ + response.setErrorResponses(this.errorResponses); + } + } + } + } + } + + public List generateMethods(Resource resource, DataTypeMappingProvider dataTypeMapper, NamingPolicyProvider nameGenerator) { + if(methods == null){ + methods = new ArrayList(); + if(getOperations() != null) { + for(EndpointOperation operation: getOperations()) { + if(!operation.isDeprecated() && areModelsAvailable(operation.getParameters(), resource, dataTypeMapper)) { + methods.add(operation.generateMethod(this, resource, dataTypeMapper, nameGenerator)); + } + } + } + } + return methods; + } + + private boolean areModelsAvailable(List modelFields, Resource resource, DataTypeMappingProvider dataTypeMapper) { + Boolean isParamSetAvailable = true; + if(modelFields == null) return true; + for(ModelField modelField : modelFields){ + if (modelField.getParamType().equalsIgnoreCase(EndpointOperation.PARAM_TYPE_BODY) ){ + isParamSetAvailable = false; + for(Model model : resource.getModels()){ + if(dataTypeMapper.isPrimitiveType(modelField.getDataType())){ + isParamSetAvailable = true; + break; + } + if(model.getName().equalsIgnoreCase(modelField.getDataType())){ + isParamSetAvailable = true; + break; + } + } + if(!isParamSetAvailable){ + return false; + } + } + } + return isParamSetAvailable; + } +} diff --git a/src/main/java/com/wordnik/swagger/codegen/resource/EndpointOperation.java b/src/main/java/com/wordnik/swagger/codegen/resource/EndpointOperation.java new file mode 100644 index 00000000000..dd63eb6a147 --- /dev/null +++ b/src/main/java/com/wordnik/swagger/codegen/resource/EndpointOperation.java @@ -0,0 +1,333 @@ +package com.wordnik.swagger.codegen.resource; + +import com.wordnik.swagger.codegen.MethodArgument; +import com.wordnik.swagger.codegen.ResourceMethod; +import com.wordnik.swagger.codegen.config.DataTypeMappingProvider; +import com.wordnik.swagger.codegen.config.NamingPolicyProvider; + +import java.util.ArrayList; +import java.util.List; + +/** + * User: ramesh + * Date: 3/31/11 + * Time: 7:54 AM + */ +public class EndpointOperation { + + public static String PARAM_TYPE_QUERY = "query"; + public static String PARAM_TYPE_PATH = "path"; + public static String PARAM_TYPE_BODY = "body"; + public static String PARAM_TYPE_HEADER = "header"; + private static String AUTH_TOKEN_PARAM_NAME = "auth_token"; + private static String API_KEY_PARAM_NAME = "api_key"; + private static String FORMAT_PARAM_NAME = "format"; + + private static String AUTH_TOKEN_ARGUMENT_NAME = "authToken"; + + private String httpMethod; + + private String summary = ""; + + private String notes = ""; + + private boolean open; + + @Deprecated + private List response; + + private String responseClass; + + private List parameters; + + private boolean deprecated; + + private ResourceMethod method; + + private List tags; + + @Deprecated + private String suggestedName; + + private String nickname; + + public String getHttpMethod() { + return httpMethod; + } + + public void setHttpMethod(String httpMethod) { + this.httpMethod = httpMethod; + } + + public String getSummary() { + return summary; + } + + public void setSummary(String summary) { + this.summary = summary; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + public boolean isOpen() { + return open; + } + + public void setOpen(boolean open) { + this.open = open; + } + + public List getResponse() { + return response; + } + + public void setResponse(List response) { + this.response = response; + } + + public String getResponseClass() { + return responseClass; + } + + public void setResponseClass(String responseClass) { + this.responseClass = responseClass; + this.setResponse(new ArrayList()); + Response response = new Response(); + response.setValueType(this.responseClass); + this.getResponse().add(response); + } + + public List getParameters() { + return parameters; + } + + public void setParameters(List parameters) { + this.parameters = parameters; + } + + public boolean isDeprecated() { + return deprecated; + } + + public void setDeprecated(boolean deprecated) { + this.deprecated = deprecated; + } + + public String getSuggestedName() { + return suggestedName; + } + + public void setSuggestedName(String suggestedName) { + this.suggestedName = suggestedName; + } + + public void setNickname(String nickname) { + this.nickname = nickname; + this.suggestedName = nickname; + } + + public String getNickname() { + return nickname; + } + + public List getTags() { + return tags; + } + + public void setTags(List tags) { + this.tags = tags; + } + + + public ResourceMethod generateMethod(Endpoint endPoint, Resource resource, DataTypeMappingProvider dataTypeMapper, NamingPolicyProvider nameGenerator) { + if(method == null){ + method = new ResourceMethod(); + //add method description + method.setDescription(this.getSummary() + "\n " + getNotes()); + + //add method name + //get resource path for making web service call + /** + * Logic for method names + * 1. remove all path parameters + * 2. Remove format path parameter + * 3. For POST add save + * 4. For PUT add update + * 5. For DELETE add delete + * 6. For GET add get + * 7. Concatenate rest of the path with init caps + * 8. + */ + + String inputobjectName = nameGenerator.getInputObjectName(resource.generateClassName(nameGenerator), endPoint.getPath()); + + String[] pathElements = endPoint.getPath().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(urlPath.length() > 0) { + urlPath.append("+"); + } + if(position < 0) { + urlPath.append("\"/"+pathElement+"\""); + }else if (position == 0) { + urlPath.append("\"/\"+"+pathElement.substring(1, pathElement.length()-1)); + }else{ + urlPath.append("\"/"+pathElement.replace("{format}", "json")+"\""); + } + } + } + } + method.setResourcePath(endPoint.getPath()); + method.setName(nameGenerator.getMethodName(endPoint.getPath(), this.getSuggestedName())); + + //create method argument + /** + * 1. API token need not be included as that is always added to the calls as HTTP headers + * 2. We need to handle auth token specially, hence need to differentiate that + * 3. Query parameters needs to be added as query string hence need to separate them out + * 4. Post parameters are usually WordnikObjects, hence we need to handle them separately + */ + List argNames = new ArrayList(); + if(this.getParameters() != null) { + List arguments = new ArrayList(); + List queryParams= new ArrayList(); + List pathParams= new ArrayList(); + method.setArguments(arguments); + method.setQueryParameters(queryParams); + method.setPathParameters(pathParams); + + for(ModelField modelField : this.getParameters()){ + if(!argNames.contains(modelField.getName())) { + argNames.add(modelField.getName()); + MethodArgument anArgument = new MethodArgument(); + anArgument.setAllowedValues(modelField.getAllowedValuesString()); + //check if arguments has auth token + if(modelField.getParamType().equalsIgnoreCase(PARAM_TYPE_HEADER) && + modelField.getName().equals(AUTH_TOKEN_PARAM_NAME)){ + method.setAuthToken(true); + anArgument.setName(AUTH_TOKEN_ARGUMENT_NAME); + anArgument.setDataType(MethodArgument.ARGUMENT_STRING); + anArgument.setDescription(modelField.getDescription()); + arguments.add(anArgument); + }else if(modelField.getParamType().equalsIgnoreCase(PARAM_TYPE_HEADER) && + modelField.getName().equals(API_KEY_PARAM_NAME)){ + //do nothing for API key parameter as all calls will automatically add API KEY to the http headers + }else if (modelField.getParamType().equalsIgnoreCase(PARAM_TYPE_PATH) && + !modelField.getName().equalsIgnoreCase(FORMAT_PARAM_NAME)) { + anArgument.setName(modelField.getName()); + anArgument.setDataType(MethodArgument.ARGUMENT_STRING); + anArgument.setDescription(modelField.getDescription()); + arguments.add(anArgument); + pathParams.add(anArgument); + }else if (modelField.getParamType().equalsIgnoreCase(PARAM_TYPE_QUERY)) { + anArgument.setName(modelField.getName()); + anArgument.setDataType(MethodArgument.ARGUMENT_STRING); + anArgument.setDescription(modelField.getDescription()); + queryParams.add(anArgument); + arguments.add(anArgument); + }else if (modelField.getParamType().equalsIgnoreCase(PARAM_TYPE_BODY)) { + if(modelField.getName() == null) { + modelField.setName("postObject"); + } + anArgument.setName(modelField.getName()); + anArgument.setDataType(dataTypeMapper.getReturnValueType(modelField.getDataType())); + anArgument.setDescription(modelField.getDescription()); + arguments.add(anArgument); + method.setPostObject(true); + } + + if(modelField.isAllowMultiple() && dataTypeMapper.isPrimitiveType(modelField.getDataType())){ + anArgument.setDataType(dataTypeMapper.getListReturnTypeSignature( + dataTypeMapper.getReturnValueType(modelField.getDataType()))); + } + anArgument.setInputModelClassArgument(inputobjectName, nameGenerator); + } + } + } + + //check for number of arguments, if we have more than 4 then send the arguments as input object + if(method.getArguments() != null && method.getArguments().size() > 4){ + List arguments = new ArrayList(); + Model modelforMethodInput = new Model(); + modelforMethodInput.setName(inputobjectName); + List fields = new ArrayList(); + for(MethodArgument argument: method.getArguments()){ + if(!argument.getName().equals("postObject") && !argument.getName().equals("authToken")){ + ModelField aModelField = new ModelField(); + aModelField.setAllowedValues(argument.getAllowedValues()); + aModelField.setDescription(argument.getDescription()); + aModelField.setName(argument.getName()); + aModelField.setParamType(argument.getDataType()); + fields.add(aModelField); + }else{ + arguments.add(argument); + } + } + modelforMethodInput.setFields(fields); + + MethodArgument anArgument = new MethodArgument(); + anArgument.setDataType(inputobjectName); + anArgument.setName(nameGenerator.applyMethodNamingPolicy(inputobjectName)); + arguments.add(anArgument); + method.setArguments(arguments); + method.setInputModel(modelforMethodInput); + } + + List argumentDefinitions = new ArrayList(); + List argumentNames = new ArrayList(); + if (method.getArguments() != null && method.getArguments().size() > 0) { + for(MethodArgument arg: method.getArguments()) { + if(!arg.getName().equalsIgnoreCase(FORMAT_PARAM_NAME)){ + argumentDefinitions.add(arg.getDataType() + " " + arg.getName()); + argumentNames.add(arg.getName()); + } + } + method.setArgumentDefinitions(argumentDefinitions); + method.setArgumentNames(argumentNames); + } + + //get method type + method.setMethodType(this.getHttpMethod()); + + //get return value + List response = this.getResponse(); + + method.setReturnValue(dataTypeMapper.getReturnValueType(response.get(0).getValueType())); + method.setReturnClassName(dataTypeMapper.getReturnClassType(response.get(0).getValueType())); + + + //get description string for exception + method.setExceptionDescription(calculateExceptionMessage()); + } + return method; + } + + /** + * Each operation can have one or many error responses Concatenate all the error responses and create on string + * @return + */ + private String calculateExceptionMessage() { + StringBuilder errorMessage = new StringBuilder(); + if(this.getResponse() != null) { + for(Response response: this.getResponse()) { + if(response.getErrorResponses() != null) { + for(ErrorResponse errorResponse : response.getErrorResponses()){ + errorMessage.append(errorResponse.getCode() + " - " + errorResponse.getReason() +" "); + } + } + } + } + return errorMessage.toString(); + } + +} diff --git a/src/main/java/com/wordnik/swagger/codegen/resource/ErrorResponse.java b/src/main/java/com/wordnik/swagger/codegen/resource/ErrorResponse.java new file mode 100644 index 00000000000..f5f1871dbd7 --- /dev/null +++ b/src/main/java/com/wordnik/swagger/codegen/resource/ErrorResponse.java @@ -0,0 +1,33 @@ +package com.wordnik.swagger.codegen.resource; + +/** + * Created by IntelliJ IDEA. + * User: ramesh + * Date: 3/31/11 + * Time: 8:24 AM + * To change this template use File | Settings | File Templates. + */ +public class ErrorResponse { + + private int code; + + private String reason; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getReason() { + return reason; + } + + public void setReason(String reason) { + this.reason = reason; + } + + +} diff --git a/src/main/java/com/wordnik/swagger/codegen/resource/Model.java b/src/main/java/com/wordnik/swagger/codegen/resource/Model.java new file mode 100644 index 00000000000..35d97cc0794 --- /dev/null +++ b/src/main/java/com/wordnik/swagger/codegen/resource/Model.java @@ -0,0 +1,49 @@ +package com.wordnik.swagger.codegen.resource; + +import java.util.List; + +/** + * Created by IntelliJ IDEA. + * User: ramesh + * Date: 3/31/11 + * Time: 8:31 AM + * To change this template use File | Settings | File Templates. + */ +public class Model { + + public static String INPUT_OBJECT_SUFFIX = "Input"; + + private String name; + + private String description; + + private List fields; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List getFields() { + return fields; + } + + public void setFields(List fields) { + this.fields = fields; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getGenratedClassName() { + return name.substring(0,1).toUpperCase() + name.substring(1); + } +} diff --git a/src/main/java/com/wordnik/swagger/codegen/resource/ModelField.java b/src/main/java/com/wordnik/swagger/codegen/resource/ModelField.java new file mode 100644 index 00000000000..656439709b6 --- /dev/null +++ b/src/main/java/com/wordnik/swagger/codegen/resource/ModelField.java @@ -0,0 +1,214 @@ +package com.wordnik.swagger.codegen.resource; + +import com.wordnik.swagger.codegen.FieldDefinition; +import com.wordnik.swagger.codegen.config.DataTypeMappingProvider; + +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +/** + * User: ramesh + * Date: 3/31/11 + * 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() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getWrapperName() { + return wrapperName; + } + + public void setWrapperName(String wrapperName) { + this.wrapperName = wrapperName; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getDefaultValue() { + return defaultValue; + } + + public void setDefaultValue(String defaultValue) { + this.defaultValue = defaultValue; + } + + public boolean isRequired() { + return required; + } + + public void setRequired(boolean required) { + this.required = required; + } + + public List getAllowableValues() { + return allowableValues; + } + + public boolean isAllowMultiple() { + return allowMultiple; + } + + public void setAllowMultiple(boolean allowMultiple) { + this.allowMultiple = allowMultiple; + } + + public void setAllowableValues(List allowableValues) { + this.allowableValues = allowableValues; + } + + public String getAllowedValuesString() { + String result = ""; + if (this.allowableValues != null) { + for(String allowedValue: this.allowableValues){ + result += (allowedValue +","); + } + } + if(result.length() == 0) + return null; + else + return result.substring(0, result.length() - 1); + } + + public void setAllowedValues(String csvAlowedValue) { + List allowedValues = new ArrayList(); + if (csvAlowedValue != null) { + StringTokenizer tokenizer = new StringTokenizer( csvAlowedValue, "," ); + while(tokenizer.hasMoreTokens()){ + tokenizer.nextToken(","); + } + } + this.setAllowableValues(allowedValues); + } + + public String getParamType() { + return paramType; + } + + public void setParamType(String paramType) { + this.paramType = paramType; + } + + public String getInternalDescription() { + return internalDescription; + } + + public void setInternalDescription(String internalDescription) { + this.internalDescription = internalDescription; + } + + public String getParamAccess() { + return paramAccess; + } + + public void setParamAccess(String paramAccess) { + this.paramAccess = paramAccess; + } + + public String getDataType() { + return dataType; + } + + public void setDataType(String dataType) { + this.dataType = dataType; + } + + public FieldDefinition getFieldDefinition(){ + return fieldDefinition; + } + + public FieldDefinition getFieldDefinition(DataTypeMappingProvider dataTypeMapper) { + if(fieldDefinition == null) { + fieldDefinition = new FieldDefinition(); + String type = paramType.trim(); + if(type.contains("date")||type.contains("Date") ){ + fieldDefinition.getImportDefinitions().add("java.util.Date"); + } + if(type.startsWith("List[")){ + fieldDefinition.getImportDefinitions().addAll(dataTypeMapper.getListImportPackages()); + String entryType = type.substring(5, type.length()-1); + entryType = dataTypeMapper.getObjectType(entryType, true); + String returnType = dataTypeMapper.getListReturnTypeSignature(entryType); + fieldDefinition.setReturnType(returnType); + fieldDefinition.setInitialization(" = " + dataTypeMapper.generateListInitialization(entryType)); + if(this.getWrapperName() != null){ + fieldDefinition.setName(this.getWrapperName()); + }else{ + fieldDefinition.setName(this.getName()); + } + + }else if(type.startsWith("Set[")){ + fieldDefinition.getImportDefinitions().addAll(dataTypeMapper.getSetImportPackages()); + String entryType = type.substring(4, type.length()-1); + entryType = dataTypeMapper.getObjectType(entryType, true); + String returnType = dataTypeMapper.getSetReturnTypeSignature(entryType); + fieldDefinition.setReturnType(returnType); + fieldDefinition.setInitialization(" = " + dataTypeMapper.generateSetInitialization(entryType)); + if(this.getWrapperName() != null){ + fieldDefinition.setName(this.getWrapperName()); + }else{ + fieldDefinition.setName(this.getName()); + } + + }else if (type.startsWith("Map[")) { + fieldDefinition.getImportDefinitions().addAll(dataTypeMapper.getMapImportPackages()); + String keyClass, entryClass = ""; + String entryType = type.substring(4, type.length()-1); + keyClass = entryType.substring(0, entryType.indexOf(",") ); + entryClass = entryType.substring(entryType.indexOf(",") + 1, entryType.length()); + //entryType = dataTypeMapper.getObjectType(entryType, true); + entryType = dataTypeMapper.getObjectType(keyClass, true) + "," + dataTypeMapper.getObjectType(entryClass, true); + String returnType = dataTypeMapper.getMapReturnTypeSignature(entryType); + fieldDefinition.setReturnType(returnType); + fieldDefinition.setInitialization("= " + dataTypeMapper.generateMapInitialization(entryType)); + if(this.getWrapperName() != null){ + fieldDefinition.setName(this.getWrapperName()); + }else{ + fieldDefinition.setName(this.getName()); + } + }else{ + fieldDefinition.setReturnType(dataTypeMapper.getObjectType(type, false)); + fieldDefinition.setName(this.getName()); + } + + } + return fieldDefinition; + } +} diff --git a/src/main/java/com/wordnik/swagger/codegen/resource/Resource.java b/src/main/java/com/wordnik/swagger/codegen/resource/Resource.java new file mode 100644 index 00000000000..55773e31ffd --- /dev/null +++ b/src/main/java/com/wordnik/swagger/codegen/resource/Resource.java @@ -0,0 +1,124 @@ +package com.wordnik.swagger.codegen.resource; + +import com.wordnik.swagger.codegen.ResourceMethod; +import com.wordnik.swagger.codegen.config.DataTypeMappingProvider; +import com.wordnik.swagger.codegen.config.NamingPolicyProvider; +import org.codehaus.jackson.annotate.JsonCreator; +import org.codehaus.jackson.annotate.JsonProperty; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * User: ramesh + * Date: 3/30/11 + * 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; + + @JsonProperty("apis") + private List endPoints = new ArrayList(); + + @JsonProperty("models") + private ApiModelListWrapper modelListWrapper; + + + private List models = new ArrayList(); + + private String generatedClassName; + + private List methods; + + @JsonCreator + public Resource() { + + } + + public String getApiVersion() { + return apiVersion; + } + + public void setApiVersion(String apiVersion) { + this.apiVersion = apiVersion; + } + + //TODO rename the JSON property too after the sandbox var has been renamed + @JsonProperty("swaggerVersion") + public String getSwaggerVersion() { + return swaggerVersion; + } + + @JsonProperty("swaggerVersion") + public void setSwaggerVersion(String swaggerVersion) { + this.swaggerVersion = swaggerVersion; + } + + @JsonProperty("apis") + public List getEndPoints() { + return endPoints; + } + + @JsonProperty("apis") + public void setEndPoints(List endPoints) { + this.endPoints = endPoints; + } + + @JsonProperty("models") + public ApiModelListWrapper getModelListWrapper() { + return modelListWrapper; + } + + @JsonProperty("models") + public void setModelListWrapper(ApiModelListWrapper modelListWrapper) { + this.modelListWrapper = modelListWrapper; + } + + public List getModels() { + return models; + } + + /*public void setModels(List models) { + this.models = models; + }*/ + + public String generateClassName(NamingPolicyProvider nameGenerator) { + if (generatedClassName == null) { + 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(); + if(getEndPoints() != null) { + for(Endpoint endpoint: getEndPoints()){ + methods.addAll(endpoint.generateMethods(resource, dataTypeMapper, nameGenerator)); + } + } + } + return methods; + } + + public void generateModelsFromWrapper(NamingPolicyProvider nameGenerator) { + String modelName; + ApiModelDefn modelDefn; + if (modelListWrapper != null) { + for (Map.Entry entry : modelListWrapper.getModelList().entrySet()) { + modelName = entry.getKey(); + modelDefn = entry.getValue(); + models.add (modelDefn.toModel(modelName, nameGenerator) ); + } + } + + } + +} diff --git a/src/main/java/com/wordnik/swagger/codegen/resource/Response.java b/src/main/java/com/wordnik/swagger/codegen/resource/Response.java new file mode 100644 index 00000000000..09d56eeb61f --- /dev/null +++ b/src/main/java/com/wordnik/swagger/codegen/resource/Response.java @@ -0,0 +1,44 @@ +package com.wordnik.swagger.codegen.resource; + + +import java.util.List; + +/** + * User: ramesh + * Date: 3/31/11 + * Time: 7:55 AM + */ +public class Response { + + private String valueType; + + private String condition; + + private List errorResponses; + + public String getValueType() { + return valueType; + } + + public void setValueType(String valueType) { + this.valueType = valueType; + } + + public String getCondition() { + return condition; + } + + public void setCondition(String condition) { + this.condition = condition; + } + + public List getErrorResponses() { + return errorResponses; + } + + public void setErrorResponses(List errorResponses) { + this.errorResponses = errorResponses; + } + + +} diff --git a/src/main/java/com/wordnik/swagger/exception/CodeGenerationException.java b/src/main/java/com/wordnik/swagger/exception/CodeGenerationException.java new file mode 100644 index 00000000000..308f4a27338 --- /dev/null +++ b/src/main/java/com/wordnik/swagger/exception/CodeGenerationException.java @@ -0,0 +1,25 @@ +package com.wordnik.swagger.exception; + +/** + * Exception raised while generating code for java driver. + * User: ramesh + * Date: 3/31/11 + * Time: 9:29 AM + */ +public class CodeGenerationException extends RuntimeException { + + private String message; + + public CodeGenerationException(String message){ + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} +