Link shortener application - return the "following" string I'm working on developing an application to shorten links. Part of that requires me to be able to get the "next" string based on an allowed character ranges, as if we were counting up. Characters go from A-Z to a-z to 0-9. Some samples:

"A" =&gt; "B"
"AA" =&gt; "AB"
"AZ" =&gt; "Aa"
"Az" =&gt; "A0"
"A9" =&gt; "BA"
"999" =&gt; "AAAA"


I've written a small class to accomplish this. My main concerns are the way I've hard-coded the ranges, and I'm not sure if doing this recursively made the most sense.

package proceduralstring;

/**
 * Char succession: A-Z a-z 0-9
 * @author User
 *
 */
public class ProceduralStringCreator {

    public static String getFirst(){
        return "A";
    }

    public static String getFirst(int minLength){
        StringBuilder result = new StringBuilder();
        for(int i=0; i&lt;minLength; i++){
            result.append("A");
        }
        return result.toString();
    }

    public static String getNext(String current) throws BadFormatException{
        if(!current.matches("[a-zA-Z0-9]+")){
            throw new BadFormatException("String contains illegal characters.");
        }
        if(current.matches("9{"+current.length()+"}")){
            return getFirst(current.length()+1);
        }
        StringBuilder result = new StringBuilder(current);
        replaceWithNext(result, result.length()-1);
        return result.toString();
    }

    private static StringBuilder replaceWithNext(StringBuilder toReplace, int index){
        char currentChar = toReplace.charAt(index);
        if(currentChar != '9'){ //base case
            if(currentChar != 'z' &amp;&amp; currentChar != 'Z'){
                toReplace.replace(index, index+1, ((char)(currentChar+1))+"");
            }else{
                if(currentChar == 'z'){
                    toReplace.replace(index, index+1, "0");
                }else if(currentChar == 'Z'){
                    toReplace.replace(index, index+1, "a");
                }
            }
            return toReplace;
        }else{ //recursive case
            toReplace.replace(index, index+1, "A");
            return replaceWithNext(toReplace, index-1);
        }
    }
}


The code is tested and works as intended (the exception is the desired behavior, as there is an invalid character):

package proceduralstring;

public class ProcStringTest {

    public static void main(String[] args) throws BadFormatException {
        System.out.println("getFirst: "+ProceduralStringCreator.getFirst());
        System.out.println("getFirst(5): "+ProceduralStringCreator.getFirst(5));
        System.out.println("Next of A: "+ProceduralStringCreator.getNext("A"));
        System.out.println("Next of AA: "+ProceduralStringCreator.getNext("AA"));
        System.out.println("Next of AAA: "+ProceduralStringCreator.getNext("AAA"));
        System.out.println("Next of A9: "+ProceduralStringCreator.getNext("A9"));
        System.out.println("Next of Adhs3hdfh9: "+ProceduralStringCreator.getNext("Adhs3hdfh9"));
        System.out.println("Next of 9: "+ProceduralStringCreator.getNext("9"));
        System.out.println("Next of 99: "+ProceduralStringCreator.getNext("99"));
        System.out.println("Next of Aas9s99: "+ProceduralStringCreator.getNext("Aas9s99"));
        System.out.println("Next of 9z9999: "+ProceduralStringCreator.getNext("9z9999"));
        System.out.println("Next of 9Z9999: "+ProceduralStringCreator.getNext("9Z9999"));
        System.out.println("Next of AUHR(#H: "+ProceduralStringCreator.getNext("AUHR(#H"));
    }

}


Output:

getFirst: A
getFirst(5): AAAAA
Next of A: B
Next of AA: AB
Next of AAA: AAB
Next of A9: BA
Next of Adhs3hdfh9: Adhs3hdfiA
Next of 9: AA
Next of 99: AAA
Next of Aas9s99: Aas9tAA
Next of 9z9999: 90AAAA
Next of 9Z9999: 9aAAAA
Exception in thread "main" proceduralstring.BadFormatException: String contains illegal characters.
    at proceduralstring.ProceduralStringCreator.getNext(ProceduralStringCreator.java:24)
    at proceduralstring.ProcStringTest.main(ProcStringTest.java:18)
Picked up JAVA_TOOL_OPTIONS: -Duser.language=en
Consider wrapping getFirst() into getNext(String) - just have it return "A" if it takes in a null or empty string. That might make it a little easier for the client to use.
getFirst(int) can leverage Arrays.fill(), which will make it a little easier to read. Consider throwing an exception if the minLength is less than zero - otherwise you get an empty string returned. Another option would be to return "A". 
The core problem can be solved non-recursively. A helper method to return the next character would be, well, helpful, and easier to read than what you're doing now. The math gets done inside the loop, and you prepend an 'A' if appropriate.
You might want to make 'A' a constant, since it's special.


If you make the changes I suggest, your code might look something like:

import java.util.Arrays;
import java.util.regex.Pattern;

public class ProceduralStringCreator {

    private static final Pattern VALID = Pattern.compile("[a-zA-Z0-9]+");
    private static final char FIRST_CHARACTER = 'A';

    public static String next(final int size) {
        if (size &lt; 0) {
            throw new IllegalArgumentException("Size must be greater than or equal to zero, was " + size);
        }
        final char[] value = new char[size];
        Arrays.fill(value, FIRST_CHARACTER);
        return new String(value);
    }

    public static String next(final String current) throws BadFormatException {
        if (current == null || current.isEmpty()) {
            return Character.toString(FIRST_CHARACTER);
        }

        if (!VALID.matcher(current).matches()) {
            throw new BadFormatException("String contains illegal characters.");
        }

        final StringBuilder stringBuilder = new StringBuilder(current);
        int index = stringBuilder.length();

        do {
            index--;
            stringBuilder.setCharAt(index, nextCharacter(stringBuilder.charAt(index)));
        } while ((index &gt;= 1) &amp;&amp; (stringBuilder.charAt(index) == FIRST_CHARACTER));

        if ((index == 0) &amp;&amp; (stringBuilder.charAt(index) == FIRST_CHARACTER)) {
            stringBuilder.insert(0, FIRST_CHARACTER);
        }

        return stringBuilder.toString();
    }

    private static char nextCharacter(final char character) {
        switch (character) {
        case 'Z':
            return 'a';
        case 'z':
            return '0';
        case '9':
            return 'A';
        default :
            return (char) (character + 1);
        }
    }

}