/** * Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.strata.collect; import java.io.Serializable; import java.util.regex.Pattern; import org.joda.convert.ToString; import com.google.common.base.CharMatcher; import com.opengamma.strata.collect.named.Named; /** * An abstract class designed to enable typed strings. * <p> * The purpose of {@code TypedString} is to provide a Java type to a concept * that might otherwise be represented as a string. * It could be thought of as a way to provide a type alias for a string. * <p> * The string wrapped by this type must not be empty. * <p> * Subclasses must be written as follows: * <pre> * public final class FooType * extends TypedString<FooType> { * private static final long serialVersionUID = 1L; * @FromString * public static FooType of(String name) { * return new FooType(name); * } * private FooType(String name) { * super(name); * } * } * </pre> * <p> * The net result is that an API can be written with methods taking * {@code FooType} as a method parameter instead of {@code String}. * * @param <T> the implementation subclass of this class */ public abstract class TypedString<T extends TypedString<T>> implements Named, Comparable<T>, Serializable { /** Serialization version. */ private static final long serialVersionUID = 1L; /** * The name. */ private final String name; /** * Creates an instance. * * @param name the name, not empty */ protected TypedString(String name) { this.name = ArgChecker.notEmpty(name, "name"); } /** * Creates an instance, validating the name against a regex. * <p> * In most cases, a {@link CharMatcher} will be faster than a regex {@link Pattern}, * typically by over an order of magnitude. * * @param name the name, not empty * @param pattern the regex pattern for validating the name * @param msg the message to use to explain validation failure */ protected TypedString(String name, Pattern pattern, String msg) { ArgChecker.notEmpty(name, "name"); ArgChecker.notNull(pattern, "pattern"); ArgChecker.notEmpty(msg, "msg"); if (pattern.matcher(name).matches() == false) { throw new IllegalArgumentException(msg); } this.name = name; } /** * Creates an instance, validating the name against a matcher. * <p> * In most cases, a {@link CharMatcher} will be faster than a regex {@link Pattern}, * typically by over an order of magnitude. * * @param name the name, not empty * @param matcher the matcher for validating the name * @param msg the message to use to explain validation failure */ protected TypedString(String name, CharMatcher matcher, String msg) { ArgChecker.notEmpty(name, "name"); ArgChecker.notNull(matcher, "pattern"); ArgChecker.notEmpty(msg, "msg"); if (matcher.matchesAllOf(name) == false) { throw new IllegalArgumentException(msg); } this.name = name; } //------------------------------------------------------------------------- /** * Gets the name. * * @return the name */ @Override public String getName() { return name; } /** * Compares this type to another. * <p> * Instances are compared in alphabetical order based on the name. * * @param other the object to compare to * @return the comparison */ @Override public final int compareTo(T other) { return name.compareTo(other.toString()); } /** * Checks if this type equals another. * <p> * Instances are compared based on the name. * * @param obj the object to compare to, null returns false * @return true if equal */ @Override public final boolean equals(Object obj) { if (obj == this) { return true; } if (obj != null && obj.getClass() == getClass()) { TypedString<?> other = (TypedString<?>) obj; return name.equals(other.name); } return false; } /** * Returns a suitable hash code. * * @return a suitable hash code */ @Override public final int hashCode() { return name.hashCode() ^ getClass().hashCode(); } /** * Returns the name. * * @return the string form, not empty */ @Override @ToString public final String toString() { return name; } }