/** * Parse a string delimited by comma into a list of object instances depending on * * <pre>itemParser</pre> * * . * * @param str String delimited by comma * @param itemParser A function to transform a string to an object. * @param <T> Type to be transformed from a string * @return List of object instances */ static <T> List<T> parseList(String str, Function<String, T> itemParser) { if (!str.contains(",")) { // if just one item return ImmutableList.of(itemParser.apply(str)); } final ImmutableList.Builder<T> fields = ImmutableList.builder(); final Stack<Character> stack = new Stack<>(); int paramStartIdx = 0; for (int i = 0; i < str.length(); i++) { char c = str.charAt(i); if (c == '<' || c == '[' || c == '(') { stack.push(c); } else if (c == '>') { Assert.assertCondition(stack.pop() == '<', "Bad signature: '%s'", str); } else if (c == ']') { Assert.assertCondition(stack.pop() == '[', "Bad signature: '%s'", str); } else if (c == ')') { Assert.assertCondition(stack.pop() == '(', "Bad signature: '%s'", str); } else if (c == ',') { if (stack.isEmpty()) { // ensure outermost type parameters fields.add(itemParser.apply(str.substring(paramStartIdx, i))); paramStartIdx = i + 1; } } } Assert.assertCondition(stack.empty(), "Bad signature: '%s'", str); if (paramStartIdx < str.length()) { fields.add(itemParser.apply(str.substring(paramStartIdx, str.length()))); } return fields.build(); }
/** * Make a field from a string representation * * @param str String * @return Field */ static Field parseField(String str) { // A field consists of an identifier and a type, and they are delimited by space. if (!str.contains(" ")) { Assert.assertCondition(false, "Bad field signature: '%s'", str); } // Stack to track the nested bracket depth Stack<Character> stack = new Stack<>(); int paramStartIdx = 0; for (int i = 0; i < str.length(); i++) { char c = str.charAt(i); if (c == '<' || c == '[' || c == '(') { stack.push(c); } else if (c == '>') { // for validation Assert.assertCondition(stack.pop() == '<', "Bad field signature: '%s'", str); } else if (c == ']') { // for validation Assert.assertCondition(stack.pop() == '[', "Bad field signature: '%s'", str); } else if (c == ')') { // for validation Assert.assertCondition(stack.pop() == '(', "Bad field signature: '%s'", str); } else if (c == ' ') { if (stack.isEmpty()) { // ensure outermost type parameters QualifiedIdentifier identifier = IdentifierUtil.makeIdentifier(str.substring(paramStartIdx, i), DefaultPolicy()); String typePart = str.substring(i + 1, str.length()); return new Field(identifier, decode(typePart)); } } } return null; }
/** * Decode a string representation to a Type. * * @param signature Type string representation * @return Type */ public static Type decode(String signature) { // termination condition in this recursion if (!(signature.contains("<") || signature.contains("(") || signature.contains("["))) { return createType( signature, ImmutableList.<Type>of(), ImmutableList.<Integer>of(), ImmutableList.<Field>of()); } final Stack<Character> stack = new Stack<>(); final Stack<Integer> spanStack = new Stack<>(); String baseType = null; for (int i = 0; i < signature.length(); i++) { char c = signature.charAt(i); if (c == '<') { if (stack.isEmpty()) { Assert.assertCondition(baseType == null, "Expected baseName to be null"); baseType = signature.substring(0, i); } stack.push('<'); spanStack.push(i + 1); } else if (c == '>') { Assert.assertCondition(stack.pop() == '<', "Bad signature: '%s'", signature); int paramStartIdx = spanStack.pop(); if (stack.isEmpty()) { // ensure outermost parameters return createType( baseType, parseList( signature.substring(paramStartIdx, i), new Function<String, Type>() { @Override public Type apply(@Nullable String s) { return decode(s); } }), ImmutableList.<Integer>of(), ImmutableList.<Field>of()); } } else if (c == '[') { if (stack.isEmpty()) { Assert.assertCondition(baseType == null, "Expected baseName to be null"); baseType = signature.substring(0, i); } stack.push('['); spanStack.push(i + 1); } else if (c == ']') { Assert.assertCondition(stack.pop() == '[', "Bad signature: '%s'", signature); int paramStartIdx = spanStack.pop(); if (stack.isEmpty()) { // ensure outermost parameters return createType( baseType, ImmutableList.<Type>of(), ImmutableList.<Integer>of(), parseList( signature.substring(paramStartIdx, i), new Function<String, Field>() { @Override public Field apply(@Nullable String s) { return parseField(s); } })); } } else if (c == '(') { if (stack.isEmpty()) { Assert.assertCondition(baseType == null, "Expected baseName to be null"); baseType = signature.substring(0, i); } stack.push('('); spanStack.push(i + 1); } else if (c == ')') { Assert.assertCondition(stack.pop() == '(', "Bad signature: '%s'", signature); int paramStartIdx = spanStack.pop(); if (stack.isEmpty()) { // ensure outermost parameters return createType( baseType, ImmutableList.<Type>of(), parseList( signature.substring(paramStartIdx, i), new Function<String, Integer>() { @Override public Integer apply(@Nullable String s) { return parseValue(s); } }), ImmutableList.<Field>of()); } } } return null; }