/* * Copyright 2012 Intuit Inc. All Rights Reserved */ package com.intuit.tank.harness.functions; /* * #%L * Intuit Tank Agent (apiharness) * %% * Copyright (C) 2011 - 2015 Intuit Inc. * %% * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * #L% */ import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.charset.Charset; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Hashtable; import java.util.List; import java.util.Random; import java.util.Stack; import java.util.UUID; import org.apache.commons.codec.binary.Base64; import org.apache.commons.jexl2.JexlContext; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import com.intuit.tank.harness.APITestHarness; import com.intuit.tank.harness.logging.LogUtil; import com.intuit.tank.logging.LogEventType; import com.intuit.tank.vm.common.util.ExpressionContextVisitor; /** * * JexlStringFunctions functions useful for String info. Functions start with #stringFunctions.functionName(parameters). * * @author dangleton * @author rchalmela * */ public class JexlStringFunctions implements ExpressionContextVisitor { private static final Logger LOG = LogManager.getLogger(JexlStringFunctions.class); /** * Preset values */ static final String[] alphaUpper = { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" }; static final String[] alphaLower = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z" }; static final String[] numeric = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }; static final String[] special = { "!", "@", "#", "$", "%", "^", "*", "{", "[", "}", "]", ":", ";", ",", ".", "?", "~", "`" }; static final String[] unicode = { "\u00a1", "\u00a2", "\u00a3", "\u00a4", "\u00a5", "\u00A9", "\u00ae", "\u00bc", "\u00bd", "\u00be", "\u00bf", "\u00e0", "\u00e1", "\u00e2", "\u00e3", "\u00e4", "\u00e5", "\u00e6" }; static Random rnd = new Random(); static private Hashtable<String, Stack<Integer>> stackMap = new Hashtable<String, Stack<Integer>>(); /** * @{inheritDoc */ @Override public void visit(JexlContext context) { context.set("stringFunctions", this); } public static void resetStatics() { stackMap.clear(); } /** * Get a random, positive whole number * * @param length * The length of the full numbers * @return A random whole number */ private String randomString(String[] values, Object olength) { int length = FunctionHandler.getInt(olength); StringBuilder output = new StringBuilder(length); for (int i = 0; i < length; i++) { output.append(values[rnd.nextInt(values.length)]); } return output.toString(); } /** * Generate a random user date consisting of a prefix and a date * * @param prefixLength * The length of the random prefix * @param dateFormat * The format of the response (MM-dd-yyyy, MMddyyyy, etc) * @return The random user id */ public String userIdFromDate(Object prefixLength, String dateFormat) { return userIdDate(prefixLength, dateFormat); } /** * Generate a random user date consisting of a prefix and a date * * @param prefixLength * The length of the random prefix * @param dateFormat * The format of the response (MM-dd-yyyy, MMddyyyy, etc) * @return The random user id */ public String userIdDate(Object oprefixLength, String dateFormat) { int prefixLength = FunctionHandler.getInt(oprefixLength); String prefix = randomString(alphaMixedNumeric(), prefixLength); String date = new SimpleDateFormat(dateFormat) .format(new java.util.Date()); return prefix + date; } /** * Generate a random user id in the range provided * * @param prefixLength * The length of the random prefix * @param dateFormat * The format for the date * @return The random user id */ public String userIdFromRange(Object ominId, Object omaxId) { int minId = FunctionHandler.getInt(ominId); int maxId = FunctionHandler.getInt(omaxId); Stack<Integer> stack = getStack(minId, maxId); if (stack.size() > 0) { return Integer.toString(stack.pop()); } throw new IllegalArgumentException( "Exhausted random User Ids. Range not large enough for the number of calls."); } /** * @param minId * @param maxId * @return */ private synchronized Stack<Integer> getStack(Integer minId, Integer maxId) { String key = getStackKey(minId, maxId); Stack<Integer> stack = stackMap.get(key); if (stack == null) { int blockSize = (maxId - minId) / APITestHarness.getInstance().getAgentRunData().getTotalAgents(); int offset = APITestHarness.getInstance().getAgentRunData().getAgentInstanceNum() * blockSize; LOG.info(LogUtil.getLogMessage("Creating userId Block starting at " + offset + " and containing " + blockSize + " entries.", LogEventType.System)); List<Integer> list = new ArrayList<Integer>(); for (int i = 0; i < blockSize; i++) { int nextNum = i + minId + offset; list.add(nextNum); } Collections.shuffle(list); // Collections.reverse(list); stack = new Stack<Integer>(); stack.addAll(list); stackMap.put(key, stack); } return stack; } private String getStackKey(Object minId, Object maxId) { return minId + "-" + maxId; } /** * Generates a string that is a concatenation of the strings, 0-n, passed in. Evaluates strings for variables and * will concat the variable value, not name, to string. * * @param values * List of Strings to concat starting at index 3 in array * @return Concatenation of strings */ public String concat(String... values) { return StringUtils.join(values); } /** * Get a substring from a string * * @param subject * The original string * @param start * The location to start at * @return */ public String substring(String subject, int start) { return substring(subject, start, -1); } /** * <p> * Gets the String that is nested in between two Strings. * * Only the first match is returned. * </p> * * <p> * A <code>null</code> input String returns <code>null</code>. A <code>null</code> open/close returns * <code>null</code> (no match). An empty ("") open and close returns an empty string. * </p> * * <pre> * StringUtils.substringBetween("wx[b]yz", "[", "]") = "b" * StringUtils.substringBetween(null, *, *) = null * StringUtils.substringBetween(*, null, *) = null * StringUtils.substringBetween(*, *, null) = null * StringUtils.substringBetween("", "", "") = "" * StringUtils.substringBetween("", "", "]") = null * StringUtils.substringBetween("", "[", "]") = null * StringUtils.substringBetween("yabcz", "", "") = "" * StringUtils.substringBetween("yabcz", "y", "z") = "abc" * StringUtils.substringBetween("yabczyabcz", "y", "z") = "abc" * </pre> * * @param subject * the String containing the substring, may be null * @param open * the String before the substring, may be null * @param close * the String after the substring, may be null * @return the substring, <code>null</code> if no match */ public String substringBetween(String subject, String open, String close) { return substringBetween(subject, open, close, 0); } /** * <p> * Gets the nth String that is nested in between two Strings. * * </p> * * <p> * A <code>null</code> input String returns <code>null</code>. A <code>null</code> open/close returns * <code>null</code> (no match). An empty ("") open and close returns an empty string. * </p> * * @param subject * the String containing the substring, may be null * @param open * the String before the substring, may be null * @param close * the String after the substring, may be null * @param index * the zero based index of the string to return. * @return the substring, <code>null</code> if no match */ public String substringBetween(String subject, String open, String close, int index) { String[] strings = internalSubstringsBetween(subject, open, close); if (index >= 0 && index < strings.length) { return strings[index]; } return null; } private String[] internalSubstringsBetween(String subject, String open, String close) { String[] ret = null; if (subject != null && open == null && close != null) { ret = new String[] { StringUtils.substringBefore(subject, close) }; } else if (subject != null && open != null && close == null) { ret = new String[] { StringUtils.substringAfterLast(subject, open) }; } else { ret = StringUtils.substringsBetween(subject, open, close); } return ret != null ? ret : new String[0]; } public String replaceBetween(String subject, String open, String close, String replace, boolean encodeData) { if (subject == null) { return subject; } if (encodeData) { // decode the string subject = fromBase64(subject); } int indexStart = 0; int indexEnd = subject.length(); if (open != null) { indexStart = subject.indexOf(open); } if (close != null) { indexEnd = subject.indexOf(close, indexStart + open.length()); } if (indexStart == -1 || indexEnd == -1) { // do nothing } else { String s1 = open != null ? subject.substring(0, (indexStart + open.length())) : ""; String s2 = close != null ? subject.substring(indexEnd) : ""; subject = s1 + replace + s2; } if (encodeData) {// encode it again subject = toBase64(subject); } return subject; } public static void main(String[] args) { String test = "Test"; int indexOf = test.indexOf("s", -1); System.out.println( indexOf); } /** * Get a substring from a string * * @param subject * The original string * @param start * The location to start at * @param stop * The location to end at; -1 if end of string * @return */ public String substring(String subject, int start, int stop) { if (stop == -1 || stop >= subject.length()) { return subject.substring(start); } return subject.substring(start, stop); } /** * Combine the alpha lower and alpha upper collections into one * * @return The combined collection */ private String[] alphaMixed() { return combineStringArrays(JexlStringFunctions.alphaLower, JexlStringFunctions.alphaUpper); } /** * Combine the alpha mixed and numeric collections into one * * @return The combined collection */ private String[] alphaMixedNumeric() { return combineStringArrays(alphaMixed(), JexlStringFunctions.numeric); } /** * Combine the alpha mixed and special collections into one * * @return The combined collection */ private String[] alphaMixedSpecial() { return combineStringArrays(alphaMixed(), JexlStringFunctions.special); } /** * Combine the alpha mixed, numeric and special collections into one * * @return The combined collection */ private String[] alphaMixedNumericSpecial() { return combineStringArrays(alphaMixedNumeric(), JexlStringFunctions.special); } /** * Combine two String arrays into one * * @param value1 * String[] One * @param value2 * String[] Two * @return One String array containing both sets of data */ private String[] combineStringArrays(String[] value1, String[] value2) { String[] output = new String[value1.length + value2.length]; int counter = 0; for (int i = 0; i < value1.length; i++) { output[counter] = value1[i]; ++counter; } for (int i = 0; i < value2.length; i++) { output[counter] = value2[i]; ++counter; } return output; } /** * Get a random string of lower case letters of given length * * @param length * @return random string */ public String randomAlphaLower(int length) { return randomString(JexlStringFunctions.alphaLower, length); } /** * Get a random string of upper case letters of given length * * @param length * @return random string */ public String randomAlphaUpper(int length) { return randomString(JexlStringFunctions.alphaUpper, length); } /** * Get a random string of lower and upper case letters of given length * * @param length * @return random string */ public String randomAlphaMixed(int length) { return randomString(alphaMixed(), length); } /** * Get a random string of numerals * * @param length * @return random string */ public String randomNumeric(int length) { return randomString(JexlStringFunctions.numeric, length); } /** * Get a random string of special characters of given length * * @param length * @return random string */ public String randomSpecial(int length) { return randomString(JexlStringFunctions.special, length); } /** * Get a random string of lower and upper case letters and numerals of given length * * @param length * @return random string */ public String randomAlphaMixedNumeric(int length) { return randomString(alphaMixedNumeric(), length); } /** * Get a random string of lower and upper case letters and special characters of given length * * @param length * @return random string */ public String randomAlphaMixedSpecial(int length) { return randomString(alphaMixedSpecial(), length); } /** * Get a random string of lower and upper case letters, numerals and special characters of given length * * @param length * @return random string */ public String randomAlphaMixedNumericSpecial(int length) { return randomString(alphaMixedNumericSpecial(), length); } /** * Returns a string's base64 encoding * * @param toEncode * @return base64 string */ public String toBase64(String toEncode) { try { byte[] bytes = toEncode.getBytes(); return new String(Base64.encodeBase64(bytes), Charset.forName("utf-8")).trim(); } catch (Exception e) { LOG.error("Error base64 encoding " + toEncode + ": " + e); } return toEncode; } /** * Returns a string's decoded from base 64 * * @param toDecode * the string to decode * @return base64 string */ public String fromBase64(String toDecode) { try { byte[] bytes = Base64.decodeBase64(toDecode.trim()); return new String(bytes, Charset.forName("utf-8")); } catch (Exception e) { LOG.error("Error base64 decoding " + toDecode + ": " + e); } return toDecode; } /** * Get a byte array's base64 endcoding * * @param bytes * @return a base64 string */ public String byteArrayToBase64(byte[] bytes) { return Base64.encodeBase64String(bytes); } /** * Encodes a String using a URL encoder * * @param toEncode * the String to encode * @return the encoded String */ public String urlEncode(String toEncode) { try { return URLEncoder.encode(toEncode, "utf-8"); } catch (Exception e) { LOG.error("Error url encoding " + toEncode + ": " + e); } return toEncode; } /** * URL Decodes the given string * * @param toDecode * the String to decode * @return the decoded string */ public String urlDecode(String toDecode) { try { return URLDecoder.decode(toDecode, "utf-8"); } catch (Exception e) { LOG.error("Error url decoding " + toDecode + ": " + e); } return toDecode; } /** * UUID random generator * * @return the new UUID string */ public String getUUID(){ //generate random UUIDs return UUID.randomUUID().toString(); } private static void log(Object aObject){ System.out.println( String.valueOf(aObject) ); } }