Presenting the SpaceKey class

In my projects, I’m using a wrapper class for the space keys, as I think this helps a lot in following the “fail as quickly as possible” recommendation to avoid exceptions in deeper parts of the code. So, when for example I get a REST call or a Macro parameter with a space key as a String, I do the validation and only advance if it passes it. When in a UI, if possible, I use dropdowns that only allow existing space keys.
This is the code:

import org.apache.commons.lang3.StringUtils;

import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * Wrapper class for Confluence Space Key
 */
public class SpaceKey {

    public static final String SPACE_KEY_CAN_NOT_BE_BLANK_MESSAGE = "Space key can not be blank";
    public static final String INVALID_SPACE_KEY_MESSAGE =
            "Space key may only consist of ASCII letters or numbers (A-Z, a-z, 0-9), or begin with ~ if it's a personal space, with a maximum length of 255 characters. Invalid space key found: ";

    private static final String SPACE_KEY_PATTERN = "(~)*[a-zA-Z\\d]{1,255}";
    private static final Pattern pattern = Pattern.compile(SPACE_KEY_PATTERN);

    private final String delegate;


    private SpaceKey(String delegate) {
        checkSpaceKey(delegate);
        if (delegate.startsWith("~")) {
            this.delegate = delegate;
        } else {
            this.delegate = delegate.toUpperCase();
        }
    }


    private void checkSpaceKey(String spaceKey) {
        if (StringUtils.isBlank(spaceKey)) {
            throw new IllegalArgumentException(SPACE_KEY_CAN_NOT_BE_BLANK_MESSAGE);
        }
        Matcher matcher = pattern.matcher(spaceKey);
        if (!matcher.matches()) {
            throw new IllegalArgumentException(INVALID_SPACE_KEY_MESSAGE + spaceKey);
        }
    }


    /**
     * Checks if a given String is a valid space key
     * @param spaceKey space key in string format
     * @return true if it is a valid space key, false otherwise
     */
    public static boolean isValid(String spaceKey) {
        if (StringUtils.isBlank(spaceKey)) {
            return false;
        }
        Matcher matcher = pattern.matcher(spaceKey);
        return matcher.matches();
    }


    /**
     * Initializes a SpaceKey from a space key in String format
     * @param spaceKey space key in string format to initialize the space key from
     * @return a verified Confluence space key
     * @throws IllegalArgumentException in case the given {@code spaceKey} does not comply with Confluence space key restrictions
     */
    public static SpaceKey fromString(String spaceKey) {
        return new SpaceKey(spaceKey);
    }


    @Override
    public boolean equals(Object o) {
        if (o == this)
            return true;
        if (!(o instanceof SpaceKey))
            return false;
        SpaceKey other = (SpaceKey) o;
        return this.delegate.equals(other.delegate);
    }


    @Override
    public int hashCode() {
        return delegate.hashCode();
    }


    @Override
    public String toString() {
        return delegate;
    }
}

You can also use the isValidSpaceKey method in com.atlassian.confluence.spaces.Space class.
That’s also used internally e.g. when you open the space administration. You might notice that, if you use the shell commands to rename spaces and use a spacekey with a not allowed format :smiley:

It’s a standard recommendation to avoid invalid states. Using a generic String instead of a custom class allows the system to work with illegal space keys, and unexpected exceptions will happen because of this. Look at this code:

public Optional<Space> getSpace(SpaceKey spaceKey) {
        SpaceLocator spaceLocator = spaceService.getKeySpaceLocator(spaceKey.toString());
        if (spaceLocator == null) {
            return Optional.empty();
        }
        Space space = spaceLocator.getSpace();
        return Optional.ofNullable(space);
    }

This way, I can always assure that I’m not calling the SpaceService with an invalid space key.

Also, the function you commented will accept as valid a space key longer than 255 characters (not really probable that something like this will happen, but it’s still a basic edge value that Atlassian didn’t test).