package org.springframework.roo.addon.plural.addon;
import static org.springframework.roo.model.RooJavaType.ROO_PLURAL;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.jvnet.inflector.Noun;
import org.springframework.roo.addon.plural.annotations.RooPlural;
import org.springframework.roo.classpath.PhysicalTypeIdentifierNamingUtils;
import org.springframework.roo.classpath.PhysicalTypeMetadata;
import org.springframework.roo.classpath.details.FieldMetadata;
import org.springframework.roo.classpath.details.MemberFindingUtils;
import org.springframework.roo.classpath.details.annotations.AnnotationAttributeValue;
import org.springframework.roo.classpath.details.annotations.AnnotationMetadata;
import org.springframework.roo.classpath.itd.AbstractItdTypeDetailsProvidingMetadataItem;
import org.springframework.roo.metadata.MetadataIdentificationUtils;
import org.springframework.roo.model.JavaSymbolName;
import org.springframework.roo.model.JavaType;
import org.springframework.roo.project.LogicalPath;
/**
* Metadata for {@link RooPlural}.
* <p>
* Note that although this class extends
* {@link AbstractItdTypeDetailsProvidingMetadataItem}, it never adds anything
* to the ITD builder, hence it never generates an ITD source file.
*
* @author Ben Alex
* @author Juan Carlos GarcĂa
* @since 1.0
*/
public class PluralMetadata extends AbstractItdTypeDetailsProvidingMetadataItem {
private static final String PROVIDES_TYPE_STRING = PluralMetadata.class.getName();
private static final String PROVIDES_TYPE = MetadataIdentificationUtils
.create(PROVIDES_TYPE_STRING);
/**
* Creates a plural identifier for the given type in the given path.
*
* @param javaType the type for which to create the identifier (required)
* @param path the path containing the type (required)
* @return a valid plural metadata instance ID
*/
public static String createIdentifier(final JavaType javaType, final LogicalPath path) {
return PhysicalTypeIdentifierNamingUtils.createIdentifier(PROVIDES_TYPE_STRING, javaType, path);
}
public static JavaType getJavaType(final String metadataIdentificationString) {
return PhysicalTypeIdentifierNamingUtils.getJavaType(PROVIDES_TYPE_STRING,
metadataIdentificationString);
}
public static String getMetadataIdentiferType() {
return PROVIDES_TYPE;
}
public static LogicalPath getPath(final String metadataIdentificationString) {
return PhysicalTypeIdentifierNamingUtils.getPath(PROVIDES_TYPE_STRING,
metadataIdentificationString);
}
public static boolean isValid(final String metadataIdentificationString) {
return PhysicalTypeIdentifierNamingUtils.isValid(PROVIDES_TYPE_STRING,
metadataIdentificationString);
}
private Map<String, String> cache;
private String plural;
public PluralMetadata(final String identifier, final JavaType aspectName,
final PhysicalTypeMetadata governorPhysicalTypeMetadata,
final PluralAnnotationValues pluralAnnotation) {
super(identifier, aspectName, governorPhysicalTypeMetadata);
Validate.isTrue(isValid(identifier), "Metadata id '%s' is invalid", identifier);
if (!isValid()) {
return;
}
plural = getPlural(pluralAnnotation);
}
@Override
public boolean equals(final Object obj) {
// We override equals because we overrode hashCode, see that method
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof PluralMetadata)) {
return false;
}
final PluralMetadata other = (PluralMetadata) obj;
return StringUtils.equals(plural, other.getPlural());
}
/**
* This method returns the plural term as per inflector. ATTENTION: this
* method does NOT take @RooPlural into account. Use getPlural(..) instead!
*
* @param term The term to be pluralized
* @param locale Locale
* @return pluralized term
*/
public static String getInflectorPlural(final String term, final Locale locale) {
try {
// ROO-3817: Uncapitalize term to obtain the plural correctly.
String termUncapitalized = StringUtils.uncapitalize(term);
String thePluralUncapitalized = Noun.pluralOf(termUncapitalized, locale);
// ROO-3817: After that, returns the plural term taking in count if the received term is capitalized or not.
if (StringUtils
.equalsIgnoreCase(term.substring(0, 1), thePluralUncapitalized.substring(0, 1))
&& !StringUtils.equals(term.substring(0, 1), thePluralUncapitalized.substring(0, 1))) {
return StringUtils.capitalize(thePluralUncapitalized);
}
return thePluralUncapitalized;
} catch (final RuntimeException re) {
// Inflector failed (see for example ROO-305), so don't pluralize it
return term;
}
}
/**
* @return the plural of the type name
*/
public String getPlural() {
return plural;
}
/**
* @param field the field to obtain plural details for (required)
* @return a guaranteed plural, computed via an annotation or Inflector
* (never returns null or an empty string)
*/
public String getPlural(final FieldMetadata field) {
Validate.notNull(field, "Field required");
// Obtain the plural from the cache, if available
final String symbolName = field.getFieldName().getSymbolName();
if (cache != null && cache.containsKey(symbolName)) {
return cache.get(symbolName);
}
// We need to build the plural
String thePlural = "";
final AnnotationMetadata annotation =
MemberFindingUtils.getAnnotationOfType(field.getAnnotations(), ROO_PLURAL);
if (annotation != null) {
// Use the plural the user defined via the annotation
final AnnotationAttributeValue<?> attribute =
annotation.getAttribute(new JavaSymbolName("value"));
if (attribute != null) {
thePlural = attribute.getValue().toString();
}
}
if ("".equals(thePlural)) {
// Manually compute the plural, as the user did not provided one
thePlural = getInflectorPlural(symbolName, Locale.ENGLISH);
}
if (cache == null) {
// Create the cache (we defer this in case there is no field plural
// retrieval ever required for this instance)
cache = new HashMap<String, String>();
}
// Populate the cache for next time
cache.put(symbolName, thePlural);
return thePlural;
}
private String getPlural(final PluralAnnotationValues pluralAnnotation) {
if (StringUtils.isNotBlank(pluralAnnotation.getValue())) {
return pluralAnnotation.getValue();
}
return getInflectorPlural(destination.getSimpleTypeName(), Locale.ENGLISH);
}
@Override
public int hashCode() {
/*
* We override hashCode because the superclass' implementation compares
* the contents of the ITD builder, and this class never modifies that
* builder; meaning that all instances have the same hash code.
* ITD-generating metadata providers like this one rely on the hash code
* changing when the underlying metadata (in our case the plural)
* changes.
*/
return plural == null ? 0 : plural.hashCode();
}
@Override
public String toString() {
final ToStringBuilder builder = new ToStringBuilder(this);
builder.append("identifier", getId());
builder.append("valid", valid);
builder.append("aspectName", aspectName);
builder.append("destinationType", destination);
builder.append("governor", governorPhysicalTypeMetadata.getId());
builder.append("plural", getPlural());
builder.append("cachedLookups", cache == null ? "[None]" : cache.keySet().toString());
return builder.toString();
}
}