UnderscoredCamelCaseResolver.java

package org.jbehave.core.io;

import static java.util.regex.Pattern.compile;

import java.util.Locale;
import java.util.regex.Matcher;

import org.jbehave.core.Embeddable;

/**
 * <p>
 * Resolves story paths converting the camel-cased Java core class to
 * lower-case underscore-separated paths e.g.:
 * "org.jbehave.core.ICanLogin.java" -> "org/jbehave/core/i_can_login.story".
 * </p>
 * <p>
 * By default, the {@link AbstractStoryPathResolver#DEFAULT_EXTENSION} is used
 * but this can be configured via the constructor so that we can resolve class
 * to use another or no extension at all, e.g.:
 * "org.jbehave.core.ICanLogin.java" -> "org/jbehave/core/i_can_login".
 * </p>
 * <p>
 * The default resolution pattern {@link #NUMBERS_AS_LOWER_CASE_LETTERS_PATTERN}
 * treats numbers as lower case letters, e.g.:
 * "org.jbehave.core.ICanLoginTo1Site.java" ->
 * "org/jbehave/core/i_can_login_to1_site"
 * </p>
 * <p>
 * Choose {@link #NUMBERS_AS_UPPER_CASE_LETTERS_PATTERN} to treat numbers as
 * upper case letters, e.g.: "org.jbehave.core.ICanLoginTo1Site.java" ->
 * "org/jbehave/core/i_can_login_to_1_site"
 * </p>
 */
public class UnderscoredCamelCaseResolver extends AbstractStoryPathResolver {

    public static final String NUMBERS_AS_LOWER_CASE_LETTERS_PATTERN = "([A-Z].*?)([A-Z]|\\z)";
    public static final String NUMBERS_AS_UPPER_CASE_LETTERS_PATTERN = "([A-Z0-9].*?)([A-Z0-9]|\\z)";
    private static final String UNDERSCORE = "_";
    private final String resolutionPattern;
    private final Locale locale;
    private String wordToRemove = "";

    public UnderscoredCamelCaseResolver() {
        this(DEFAULT_EXTENSION);
    }

    public UnderscoredCamelCaseResolver(String extension) {
        this(extension, NUMBERS_AS_LOWER_CASE_LETTERS_PATTERN);
    }

    public UnderscoredCamelCaseResolver(String extension,
            String resolutionPattern) {
        this(extension, resolutionPattern, Locale.getDefault());
    }

    public UnderscoredCamelCaseResolver(String extension,
            String resolutionPattern, Locale locale) {
        super(extension);
        this.resolutionPattern = resolutionPattern;
        this.locale = locale;
    }

    @Override
    protected String resolveName(
            Class<? extends Embeddable> embeddableClass) {
        String simpleName = embeddableClass.getSimpleName();
        simpleName = simpleName.replace(wordToRemove, "");
        Matcher matcher = compile(resolutionPattern).matcher(
                simpleName);
        int startAt = 0;
        StringBuilder builder = new StringBuilder();
        while (matcher.find(startAt)) {
            builder.append(matcher.group(1).toLowerCase(locale));
            builder.append(UNDERSCORE);
            startAt = matcher.start(2);
        }
        return builder.substring(0, builder.length() - 1);
    }

    public StoryPathResolver removeFromClassName(String wordToRemove) {
        this.wordToRemove = wordToRemove;
        return this;
    }
}