package org.springframework.roo.classpath.customdata.taggers; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Service; import org.jvnet.inflector.Noun; import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetails; import org.springframework.roo.classpath.details.ConstructorMetadata; import org.springframework.roo.classpath.details.FieldMetadata; import org.springframework.roo.classpath.details.MemberHoldingTypeDetails; import org.springframework.roo.classpath.details.MethodMetadata; import org.springframework.roo.classpath.scanner.MemberDetails; import org.springframework.roo.classpath.scanner.MemberDetailsBuilder; import org.springframework.roo.model.CustomDataAccessor; /** * An implementation of {@link CustomDataKeyDecorator}. * * @author James Tyrrell * @author Juan Carlos GarcĂ­a * @since 1.1.3 */ @Component @Service public class CustomDataKeyDecoratorImpl implements CustomDataKeyDecorator { private final Map<String, String> pluralMap = new HashMap<String, String>(); private final Map<String, Matcher<? extends CustomDataAccessor>> taggerMap = new HashMap<String, Matcher<? extends CustomDataAccessor>>(); public MemberDetails decorate(final String requestingClass, final MemberDetails memberDetails) { final MemberDetailsBuilder memberDetailsBuilder = new MemberDetailsBuilder(memberDetails); for (final MemberHoldingTypeDetails memberHoldingTypeDetails : memberDetails.getDetails()) { if (memberHoldingTypeDetails instanceof ClassOrInterfaceTypeDetails) { if (!pluralMap.containsKey(memberHoldingTypeDetails.getDeclaredByMetadataId())) { pluralMap.put( memberHoldingTypeDetails.getDeclaredByMetadataId(), getInflectorPlural(memberHoldingTypeDetails.getName().getSimpleTypeName(), Locale.ENGLISH)); } } } // Locate any requests that we add custom data to identifiable java // structures for (final FieldMatcher fieldTagger : getFieldTaggers()) { for (final FieldMetadata field : fieldTagger.matches(memberDetails.getDetails())) { memberDetailsBuilder.tag(field, fieldTagger.getCustomDataKey(), fieldTagger.getTagValue(field)); } } for (final MethodMatcher methodTagger : getMethodTaggers()) { for (final MethodMetadata method : methodTagger .matches(memberDetails.getDetails(), pluralMap)) { memberDetailsBuilder.tag(method, methodTagger.getCustomDataKey(), methodTagger.getTagValue(method)); } } for (final ConstructorMatcher constructorTagger : getConstructorTaggers()) { for (final ConstructorMetadata constructor : constructorTagger.matches(memberDetails .getDetails())) { memberDetailsBuilder.tag(constructor, constructorTagger.getCustomDataKey(), constructorTagger.getTagValue(constructor)); } } for (final TypeMatcher typeTagger : getTypeTaggers()) { for (final MemberHoldingTypeDetails typeDetails : typeTagger.matches(memberDetails .getDetails())) { memberDetailsBuilder.tag(typeDetails, typeTagger.getCustomDataKey(), typeTagger.getTagValue(typeDetails)); } } return memberDetailsBuilder.build(); } public MemberDetails decorateTypes(final String requestingClass, final MemberDetails memberDetails) { final MemberDetailsBuilder memberDetailsBuilder = new MemberDetailsBuilder(memberDetails); for (final TypeMatcher typeTagger : getTypeTaggers()) { for (final MemberHoldingTypeDetails typeDetails : typeTagger.matches(memberDetails .getDetails())) { memberDetailsBuilder.tag(typeDetails, typeTagger.getCustomDataKey(), typeTagger.getTagValue(typeDetails)); } } return memberDetailsBuilder.build(); } public List<ConstructorMatcher> getConstructorTaggers() { final List<ConstructorMatcher> constructorTaggers = new ArrayList<ConstructorMatcher>(); for (final Matcher<? extends CustomDataAccessor> matcher : taggerMap.values()) { if (matcher instanceof ConstructorMatcher) { constructorTaggers.add((ConstructorMatcher) matcher); } } return constructorTaggers; } public List<FieldMatcher> getFieldTaggers() { final List<FieldMatcher> fieldTaggers = new ArrayList<FieldMatcher>(); for (final Matcher<? extends CustomDataAccessor> matcher : taggerMap.values()) { if (matcher instanceof FieldMatcher) { fieldTaggers.add((FieldMatcher) matcher); } } return fieldTaggers; } /** * 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 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; } } public List<MethodMatcher> getMethodTaggers() { final List<MethodMatcher> methodTaggers = new ArrayList<MethodMatcher>(); for (final Matcher<? extends CustomDataAccessor> matcher : taggerMap.values()) { if (matcher instanceof MethodMatcher) { methodTaggers.add((MethodMatcher) matcher); } } return methodTaggers; } public List<TypeMatcher> getTypeTaggers() { final List<TypeMatcher> typeTaggers = new ArrayList<TypeMatcher>(); for (final Matcher<? extends CustomDataAccessor> matcher : taggerMap.values()) { if (matcher instanceof TypeMatcher) { typeTaggers.add((TypeMatcher) matcher); } } return typeTaggers; } public void registerMatcher(final String addingClass, final Matcher<? extends CustomDataAccessor> matcher) { Validate.notNull(addingClass, "The calling class must be specified"); Validate.notNull(matcher, "The matcher must be specified"); taggerMap.put(addingClass + matcher.getCustomDataKey(), matcher); } public void registerMatchers(final Class<?> addingClass, final Matcher<? extends CustomDataAccessor>... matchers) { if (addingClass != null) { for (final Matcher<? extends CustomDataAccessor> matcher : matchers) { // We don't keep a reference to the class, as OSGi might unload // it later registerMatcher(addingClass.getName(), matcher); } } } public void unregisterMatchers(final Class<?> addingClass) { unregisterMatchers(addingClass.getName()); } public void unregisterMatchers(final String addingClass) { final Set<String> toRemove = new HashSet<String>(); for (final String taggerKey : taggerMap.keySet()) { if (taggerKey.startsWith(addingClass)) { toRemove.add(taggerKey); } } for (final String taggerKey : toRemove) { taggerMap.remove(taggerKey); } } }