Java tutorial
/* * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. */ package com.phoenixnap.oss.ramlapisync.naming; import com.phoenixnap.oss.ramlapisync.raml.RamlAction; import com.phoenixnap.oss.ramlapisync.raml.RamlActionType; import com.phoenixnap.oss.ramlapisync.raml.RamlResource; import org.jsonschema2pojo.util.NameHelper; import org.raml.parser.utils.Inflector; import org.springframework.http.MediaType; import org.springframework.util.StringUtils; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Class containing methods relating to naming converntions and string cleanup for naming * * @author Kurt Paris * @since 0.0.1 * */ public class NamingHelper { private static final Pattern CLASS_SUFFIXES_TO_CLEAN = Pattern .compile("^(.+)(services|service|impl|class|controller)", Pattern.CASE_INSENSITIVE); private static final Pattern CONTENT_TYPE_VERSION = Pattern.compile("[^v]*(v[\\d\\.]*).*", Pattern.CASE_INSENSITIVE); private static NameHelper cachedNameHelper; private static NameHelper getNameHelper() { if (cachedNameHelper != null) { return cachedNameHelper; } cachedNameHelper = new NameHelper(SchemaHelper.getDefaultGenerationConfig()); return cachedNameHelper; } /** * Converts an http contentType into a qualifier that can be used within a Java method * * @param contentType The content type to convert application/json * @return qualifier, example V1Html */ public static String convertContentTypeToQualifier(String contentType) { //lets start off simple since qualifers are better if they are simple :) //if we have simple standard types lets add some heuristics if (contentType.equals(MediaType.APPLICATION_JSON_VALUE)) { return "AsJson"; } if (contentType.equals(MediaType.APPLICATION_OCTET_STREAM_VALUE)) { return "AsBinary"; } if (contentType.equals(MediaType.TEXT_PLAIN_VALUE) || contentType.equals(MediaType.TEXT_HTML_VALUE)) { return "AsText"; } //we have a non standard type. lets see if we have a version Matcher versionMatcher = CONTENT_TYPE_VERSION.matcher(contentType); if (versionMatcher.find()) { String version = versionMatcher.group(1); if (version != null) { return StringUtils.capitalize(version).replace(".", "_"); } } //if we got here we have some sort of funky content type. deal with it int seperatorIndex = contentType.indexOf("/"); if (seperatorIndex != -1 && seperatorIndex < contentType.length()) { String candidate = contentType.substring(seperatorIndex + 1).toLowerCase(); String out = ""; if (candidate.contains("json")) { candidate = candidate.replace("json", ""); out += "AsJson"; } candidate = StringUtils.deleteAny(candidate, " ,.+=-'\"\\|~`#$%^&\n\t"); if (StringUtils.hasText(candidate)) { out = StringUtils.capitalize(candidate) + out; } return "_" + out; } return ""; } /** * Checks if a Resource URI fragment is a URI Parameter. URI parameters are defined as {myParameter} * * @param resource The Resource key/ relative URL * @return If true this URI is a frament containing a URI parameter */ public static boolean isUriParamResource(String resource) { if (resource == null) { return false; } resource = NamingHelper.cleanLeadingAndTrailingNewLineAndChars(resource.toLowerCase()); if (resource.startsWith("{") && resource.endsWith("}")) { return true; } else { return false; } } /** * Utility method to check if a string can be used as a valid class name * * @param input String to check * @return true if valid */ public static boolean isValidJavaClassName(String input) { if (!StringUtils.hasText(input)) { return false; } if (!Character.isJavaIdentifierStart(input.charAt(0))) { return false; } if (input.length() > 1) { for (int i = 1; i < input.length(); i++) { if (!Character.isJavaIdentifierPart(input.charAt(i))) { return false; } } } return true; } /** * Utility method to clean a string for use within javadoc * * @param input String to be cleaned * @return The cleaned string */ public static String cleanForJavadoc(String input) { return cleanLeadingAndTrailingNewLineAndChars(input); } /** * Utility method to clean New Line,Spaces and other highly useless characters found (mainly in javadoc) * * @param input The string to be cleaned * @return Cleaned String */ public static String cleanLeadingAndTrailingNewLineAndChars(String input) { if (!StringUtils.hasText(input)) { return input; } String output = input; output = output.replaceAll("[\\s]+\\*[\\s]+", " "); while (output.startsWith("/") || output.startsWith("\n") || output.startsWith("*") || output.startsWith("-") || output.startsWith("\t") || output.startsWith(" ") || output.startsWith("\\")) { output = output.substring(1); } while (output.endsWith("/") || output.endsWith("\n") || output.endsWith(" ") || output.endsWith(",") || output.endsWith("\t") || output.endsWith("-") || output.endsWith("*")) { output = output.substring(0, output.length() - 1); } return output; } /** * Convert a class name into its restful Resource representation. * * eg. MonitorServiceImpl becomes Monitor * * @param clazz The Class to name * @return The name for this class */ public static String convertClassName(Class<?> clazz) { String convertedName = clazz.getSimpleName(); boolean clean = true; do { Matcher cleaner = CLASS_SUFFIXES_TO_CLEAN.matcher(convertedName); if (cleaner.matches()) { if (cleaner.group(1) != null && cleaner.group(1).length() > 0) { convertedName = cleaner.group(1); } } else { clean = false; } } while (clean); return StringUtils.uncapitalize(convertedName); } /** * Attempts to load system propertes from the string or use included defaults if available * * @param inputString Strign containing spring property format * @return resolved String */ public static String resolveProperties(String inputString) { if (!StringUtils.hasText(inputString)) { return inputString; } String tempString = inputString.trim(); String outString = ""; int startIndex = 0; while (tempString.indexOf("${", startIndex) != -1) { int startsWithPos = tempString.indexOf("${", startIndex); int endsWithPos = tempString.indexOf("}", startsWithPos + 2); int nextBracket = tempString.indexOf("{", startsWithPos + 2); if (nextBracket != -1 && endsWithPos > nextBracket) { endsWithPos = tempString.indexOf("}", endsWithPos + 1); } int defaultPos = tempString.lastIndexOf(":", endsWithPos); if (defaultPos < startsWithPos) { defaultPos = -1; } if (startsWithPos != -1 && endsWithPos != -1) { String value = tempString.substring(startsWithPos + 2, endsWithPos); String defaultString; String key; if (defaultPos != -1) { //lets get default. defaultString = value.substring(value.lastIndexOf(":") + 1); key = value.substring(0, value.lastIndexOf(":")); } else { key = value; defaultString = value; } outString += tempString.substring(startIndex, startsWithPos) + System.getProperty(key, defaultString); startIndex = endsWithPos + 1; } } if (startIndex < tempString.length()) { outString += tempString.substring(startIndex); } return outString; } /** * Attempts to infer the name of a resource from a resources's relative URL * * @param resource The raml resource being parsed * @param singularize indicates if the resource name should be singularized or not * @return A name representing this resource or null if one cannot be inferred */ public static String getResourceName(RamlResource resource, boolean singularize) { String url = resource.getRelativeUri(); if (StringUtils.hasText(url)) { if (url.contains("/") && (url.lastIndexOf("/") < url.length())) { return getResourceName(url.substring(url.lastIndexOf("/") + 1), singularize); } } return null; } /** * Attempts to infer the name of a resource from a resources's relative URL * * @param resource The Url representation of this object * @param singularize indicates if the resource name should be singularized or not * @return A name representing this resource or null if one cannot be inferred */ public static String getResourceName(String resource, boolean singularize) { if (StringUtils.hasText(resource)) { String resourceName = StringUtils.capitalize(resource); if (singularize) { resourceName = Inflector.singularize(resourceName); } resourceName = cleanNameForJava(resourceName); return resourceName; } return null; } /** * Converts the name of a parameter into a name suitable for a Java parameter * * @param name The name of a RAML query parameter or request header * @return A name suitable for a Java parameter */ public static String getParameterName(String name) { return StringUtils.uncapitalize(cleanNameForJava(name)); } private static String cleanNameForJava(String resourceName) { String outString = resourceName; if (StringUtils.hasText(resourceName)) { outString = getNameHelper().normalizeName(resourceName); if (StringUtils.hasText(outString)) { outString = outString.replaceAll(NameHelper.ILLEGAL_CHARACTER_REGEX, ""); } } return outString; } /** * Attempts to infer the name of an action (intent) from a resource's relative URL and action details * * @param controllerizedResource The resource that is mapped to the root controller * @param resource The child resource that will be mapped as a method of the root controller * @param action The RAML action object * @param actionType The ActionType/HTTP Verb for this Action * @return The java name of the method that will represent this Action */ public static String getActionName(RamlResource controllerizedResource, RamlResource resource, RamlAction action, RamlActionType actionType) { String url = resource.getUri(); //Since this will be part of a resource/controller, remove the parent portion of the URL if enough details remain //to infer a meaningful method name if (controllerizedResource != resource && StringUtils.countOccurrencesOf(url, "{") < StringUtils.countOccurrencesOf(url, "/") - 1) { url = url.replace(controllerizedResource.getUri(), ""); } //sanity check if (StringUtils.hasText(url)) { //Split the url into segments by seperator String[] splitUrl = url.split("/"); String name = ""; int nonIdsParsed = 0; int index = splitUrl.length - 1; boolean singularizeNext = false; //Parse segments until end is reached or we travers a maximum of 2 non Path Variable segments, these 2 should both have at least 1 //id path variable each otherwise they would have been typically part of the parent controller //or we have REALLY long URL nesting which isnt really a happy place. while (nonIdsParsed < 2 && index >= 0) { String segment = splitUrl[index]; //Lets check for ID path variables if (segment.contains("{") && segment.contains("}")) { //should we add this to Method name //peek if (index > 0 && index == splitUrl.length - 1) { String peek = splitUrl[index - 1].toLowerCase(); if (segment.toLowerCase().contains(Inflector.singularize(peek))) { //this is probably the Id name = name + "ById"; } else { name = name + "By" + StringUtils.capitalize(segment.substring(1, segment.length() - 1)); } } //Since we probably found an ID, it means that method acts on a single resource in the collection. probably :) singularizeNext = true; } else { segment = cleanNameForJava(segment); if (singularizeNext) { //consume singularisation if (!segment.endsWith("details")) { name = Inflector.singularize(StringUtils.capitalize(segment)) + name; } else { name = StringUtils.capitalize(segment) + name; } singularizeNext = false; } else { name = StringUtils.capitalize(segment) + name; } nonIdsParsed++; if (singularizeNext) { //consume singularisation singularizeNext = false; } } index--; } //Add the http verb into the mix boolean collection = false; String tail = splitUrl[splitUrl.length - 1]; if (!Inflector.singularize(tail).equals(tail) && !tail.endsWith("details")) { collection = true; } String prefix = convertActionTypeToIntent(actionType, collection); if (collection && RamlActionType.POST.equals(actionType)) { name = Inflector.singularize(name); } return prefix + name; } //Poop happened. return nothing return null; } /** * Attempts to convert the Http Verb into a textual representation of Intent based on REST conventions * * @param actionType The ActionType/Http verb of the action * @param isTargetCollection True if this action is being done on a collection or plural resource (eg: books) * @return */ private static String convertActionTypeToIntent(RamlActionType actionType, boolean isTargetCollection) { switch (actionType) { case DELETE: return "delete"; case GET: return "get"; case POST: if (isTargetCollection) { return "create"; } case PUT: return "update"; case PATCH: return "modify"; default: return "do"; } } /** * Returns the default sub package that will be used for model objects used in the Request/Response body * @return the package suffix to be appended. */ public static String getDefaultModelPackage() { return ".model"; } }