package org.springframework.roo.metadata; import org.apache.commons.lang3.StringUtils; /** * Utility methods relating to metadata identification strings. * <p> * We use identification strings for metadata in order to reduce memory * consumption and garbage collection overhead. Strings also have the advantage * of being immutable and easy to display as text. * <p> * Metadata identification strings can identify either: * <ul> * <li>a class of {@link MetadataItem} (in which case * {@link #isIdentifyingClass(String)} returns <code>true</code>), or</li> * <li>a specific instance of such a class (in which case * {@link #isIdentifyingInstance(String)} returns <code>true</code>)</li> * </ul> * * @author Ben Alex * @since 1.0 */ public final class MetadataIdentificationUtils { /* * This delimiter was chosen because it never appears in a Java type name, * is uncommon to use in file system paths, and looks OK to a human in a * metadata id string. The first instance of this character in a given MID * separates the metadata class name from the name of the project type to * which the metadata applies. */ static final String INSTANCE_DELIMITER = "#"; // All MIDs start with these characters private static final char[] MID_PREFIX_CHARACTERS = {'M', 'I', 'D', ':'}; static final String MID_PREFIX = String.valueOf(MID_PREFIX_CHARACTERS); private static final int MID_PREFIX_LENGTH = MID_PREFIX_CHARACTERS.length; /** * Returns the class-level ID for the given type of metadata * * @param metadataClass the metadata class for which to create an ID (can be * <code>null</code>) * @return a non-blank metadata ID, or <code>null</code> if a * <code>null</code> class was given * @since 1.2.0 */ public static String create(final Class<?> metadataClass) { if (metadataClass == null) { return null; } return create(metadataClass.getName()); } /** * Creates a class-specific metadata id for the given fully qualified class * name. * <p> * You can acquire a fully qualified class name using * {@link Class#getName()}, although it's more typesafe to call * {@link #create(Class)}. * * @param fullyQualifiedClassName to create (can be null or empty) * @return the metadata identification string (may be null if the input was * invalid) */ public static String create(final String fullyQualifiedClassName) { if (StringUtils.isBlank(fullyQualifiedClassName) || fullyQualifiedClassName.contains(INSTANCE_DELIMITER)) { return null; } return MID_PREFIX + fullyQualifiedClassName; } /** * Creates an instance-specific metadata identification string for the * presented class/key pair. * * @param fullyQualifiedMetadataClass * @param instanceIdentificationKey * @return <code>null</code> if either of the given strings is blank or the * metadata class name is not well-formed */ public static String create(final String fullyQualifiedMetadataClass, final String instanceIdentificationKey) { if (StringUtils.isBlank(instanceIdentificationKey) || StringUtils.isBlank(fullyQualifiedMetadataClass) || fullyQualifiedMetadataClass.contains(INSTANCE_DELIMITER)) { return null; } final StringBuilder mid = new StringBuilder(); mid.append(MID_PREFIX); mid.append(fullyQualifiedMetadataClass); mid.append(INSTANCE_DELIMITER); mid.append(instanceIdentificationKey); return mid.toString(); } /** * Indicates the class of metadata a particular string represents. The class * will be returned even if the metadata identification string represents a * specific instance. * * @param metadataId to evaluate (can be null or empty) * @return the class only, or null if the identification string is invalid * in some way */ public static String getMetadataClass(final String metadataId) { if (!isValid(metadataId) || metadataId.equals(MID_PREFIX + INSTANCE_DELIMITER)) { return null; } final int delimiterIndex = metadataId.indexOf(INSTANCE_DELIMITER); if (delimiterIndex == -1) { // No specific metadata instance was identified, so return // everything except "MID:" return metadataId.substring(MID_PREFIX_LENGTH); } // A particular instance was identified, so we only return the instance // name part return metadataId.substring(MID_PREFIX_LENGTH, delimiterIndex); } /** * Returns the ID of the given metadata's class. * * @param metadataId the metadata ID for which to return the class ID (can * be blank) * @return <code>null</code> if a blank ID is given, otherwise a valid * class-level ID * @since 1.2.0 */ public static String getMetadataClassId(final String metadataId) { return create(getMetadataClass(metadataId)); } /** * Returns the instance key from the given metadata instance ID. * * @param metadataId the MID to evaluate (can be blank) * @return the instance ID only, or <code>null</code> if the identification * string is invalid in some way */ public static String getMetadataInstance(final String metadataId) { if (isIdentifyingInstance(metadataId)) { return metadataId.substring(metadataId.indexOf(INSTANCE_DELIMITER) + 1); } return null; } /** * Indicates whether the argument appears to represent a particular metadata * identification class. This method returns false if a particular instance * is identified. * * @param metadataIdentificationString to evaluate (can be null or empty) * @return true if the string is identifying a class of {@link MetadataItem} */ public static boolean isIdentifyingClass(final String metadataIdentificationString) { return isValid(metadataIdentificationString) && !metadataIdentificationString.contains(INSTANCE_DELIMITER); } /** * Indicates whether the argument appears to represent a specific metadata * instance. * * @param metadataIdentificationString to evaluate (can be null or empty) * @return true if the string is identifying a specific instance of a * {@link MetadataItem} */ public static boolean isIdentifyingInstance(final String metadataIdentificationString) { return isValid(metadataIdentificationString) && metadataIdentificationString.contains(INSTANCE_DELIMITER) && !metadataIdentificationString.endsWith(INSTANCE_DELIMITER); } /** * Indicates whether the argument is a well-formed metadata identification * string. This does not guarantee that it is valid, i.e. that the * identified metadata actually exists or could ever exist. * * @param metadataIdentificationString to evaluate (can be null or empty) * @return <code>true</code> if the string appears to be a valid metadata * identification string */ public static boolean isValid(final String metadataIdentificationString) { /* * According to the first comment on ROO-1932, the algorithm below is an * optimisation over simply checking for null and calling * String#startsWith(). */ if (metadataIdentificationString == null || metadataIdentificationString.length() <= MID_PREFIX_LENGTH) { return false; } for (int i = 0; i < MID_PREFIX_LENGTH; i++) { if (metadataIdentificationString.charAt(i) != MID_PREFIX_CHARACTERS[i]) { return false; } } return true; } /** * Constructor is private to prevent instantiation */ private MetadataIdentificationUtils() {} }