/** * This method scans the supplied class for PrettyFaces annotations. The method must be called for * every class that should be scanner before finally calling {@link #build(PrettyConfigBuilder)}. * * @param clazz The class to scan */ public void processClass(final Class clazz) { // log class name on trace level if (log.isTraceEnabled()) { log.trace("Analyzing class: " + clazz.getName()); } try { // scan for PrettyAnnotation class // returns the mapping ID, if an annotation was found String[] classMappingIds = processClassMappingAnnotations(clazz); // scan for PrettyBean annotation processPrettyBeanAnnotation(clazz); // process annotations on public methods for (Method method : clazz.getMethods()) { processMethodAnnotations(method, classMappingIds); } // loop over fields to find URLQueryParameter annotations for (Field field : clazz.getDeclaredFields()) { processFieldAnnotations(field, classMappingIds); } } catch (NoClassDefFoundError e) { // reference to another class unknown to the classloader log.debug("Unable to process class '" + clazz.getName() + "': " + e.toString()); } }
/** * Checks the class for a {@link URLBeanName} annotation. * * @param clazz Class to scan */ private void processPrettyBeanAnnotation(final Class clazz) { // get reference to @URLMapping annotation URLBeanName prettyBean = (URLBeanName) clazz.getAnnotation(URLBeanName.class); // process annotation if it exists if (prettyBean != null) { // log class name if (log.isTraceEnabled()) { log.trace("Found @URLBeanName annotation on class: " + clazz.getName()); } // add bean to map beanNameMap.put(clazz, prettyBean.value()); } }
/** * Creates a {@link PrettyExpression} for a class and component. This method may return a {@link * ConstantExpression} or a {@link LazyExpression}. * * @param clazz The class of the bean * @param component the component (property or method name) * @return The expression */ private PrettyExpression buildPrettyExpression(final Class<?> clazz, final String component) { if (log.isTraceEnabled()) { log.trace("Searching name of bean: " + clazz.getName()); } // get name from internal map build from @URLBeanName annotations and // previously resolved names String beanName = beanNameMap.get(clazz); // return a constant expression if (beanName != null) { if (log.isTraceEnabled()) { log.trace("Got bean name from @URLBeanName annotation: " + beanName); } return new ConstantExpression("#{" + beanName + "." + component + "}"); } // build a lazy expression else { if (log.isTraceEnabled()) { log.trace("Name of bean not found. Building lazy expression for: " + clazz.getName()); } return new LazyExpression(beanNameFinder, clazz, component); } }
@SuppressWarnings({"unchecked", "rawtypes"}) public class PrettyAnnotationHandler { /** The logger */ private static final Logger log = Logger.getLogger(PrettyAnnotationHandler.class); /** A map assigning mapping IDs to {@link UrlMapping} instances */ private final Map<String, UrlMapping> urlMappings = new HashMap<String, UrlMapping>(); /** A map to resolve the bean name for a {@link Class} object */ private final Map<Class, String> beanNameMap = new HashMap<Class, String>(); /** The {@link ActionSpec} objects generated from annotation scanning */ private final List<ActionSpec> urlActions = new ArrayList<ActionSpec>(); /** The {@link QueryParamSpec} objects generated from annotation scanning */ private final List<QueryParamSpec> queryParamList = new ArrayList<QueryParamSpec>(); /** Reference to the {@link LazyBeanNameFinder} */ private final LazyBeanNameFinder beanNameFinder; /** Constructor */ public PrettyAnnotationHandler(final LazyBeanNameFinder beanNameFinder) { this.beanNameFinder = beanNameFinder; } /** * This method scans the supplied class for PrettyFaces annotations. The method must be called for * every class that should be scanner before finally calling {@link #build(PrettyConfigBuilder)}. * * @param clazz The class to scan */ public void processClass(final Class clazz) { // log class name on trace level if (log.isTraceEnabled()) { log.trace("Analyzing class: " + clazz.getName()); } try { // scan for PrettyAnnotation class // returns the mapping ID, if an annotation was found String[] classMappingIds = processClassMappingAnnotations(clazz); // scan for PrettyBean annotation processPrettyBeanAnnotation(clazz); // process annotations on public methods for (Method method : clazz.getMethods()) { processMethodAnnotations(method, classMappingIds); } // loop over fields to find URLQueryParameter annotations for (Field field : clazz.getDeclaredFields()) { processFieldAnnotations(field, classMappingIds); } } catch (NoClassDefFoundError e) { // reference to another class unknown to the classloader log.debug("Unable to process class '" + clazz.getName() + "': " + e.toString()); } } /** * Checks for PrettyFaces mapping annotations on a single class * * @param clazz Class to scan * @return The IDs of the mappings found on the class */ public String[] processClassMappingAnnotations(final Class clazz) { // list of all mapping IDs found on the class List<String> classMappingIds = new ArrayList<String>(); // get reference to @URLMapping annotation URLMapping mappingAnnotation = (URLMapping) clazz.getAnnotation(URLMapping.class); // process annotation if it exists if (mappingAnnotation != null) { String mappingId = processPrettyMappingAnnotation(clazz, mappingAnnotation); classMappingIds.add(mappingId); } // container annotation URLMappings mappingsAnnotation = (URLMappings) clazz.getAnnotation(URLMappings.class); if (mappingsAnnotation != null) { // process all contained @URLMapping annotations for (URLMapping child : mappingsAnnotation.mappings()) { String mappingId = processPrettyMappingAnnotation(clazz, child); classMappingIds.add(mappingId); } } // return list of mappings found return classMappingIds.toArray(new String[classMappingIds.size()]); } /** * Process a single {@link URLMapping} annotation. * * @param clazz The class that the annotation was found on * @param mappingAnnotation The annotation to process * @return The mapping ID of the mapping found */ private String processPrettyMappingAnnotation( final Class clazz, final URLMapping mappingAnnotation) { // log class name if (log.isTraceEnabled()) { log.trace("Found @URLMapping annotation on class: " + clazz.getName()); } // create UrlMapping from annotation UrlMapping mapping = new UrlMapping(); mapping.setId(mappingAnnotation.id()); mapping.setParentId(mappingAnnotation.parentId()); mapping.setPattern(mappingAnnotation.pattern()); mapping.setViewId(mappingAnnotation.viewId()); mapping.setOutbound(mappingAnnotation.outbound()); mapping.setOnPostback(mappingAnnotation.onPostback()); // register mapping Object existingMapping = urlMappings.put(mapping.getId(), mapping); // fail if a mapping with this ID already existed if (existingMapping != null) { throw new IllegalArgumentException("Duplicated mapping id: " + mapping.getId()); } // At bean name to lookup map if it has been specified if ((mappingAnnotation.beanName() != null) && (mappingAnnotation.beanName().length() > 0)) { beanNameMap.put(clazz, mappingAnnotation.beanName()); } // process validations for (URLValidator validationAnnotation : mappingAnnotation.validation()) { // index attribute is required in this case if (validationAnnotation.index() < 0) { throw new IllegalArgumentException( "Please set the index of the path parameter you want to validate with the @URLValidator specified on mapping: " + mapping.getId()); } // prepare PathValidator PathValidator pathValidator = new PathValidator(); pathValidator.setIndex(validationAnnotation.index()); pathValidator.setOnError(validationAnnotation.onError()); pathValidator.setValidatorIds(join(validationAnnotation.validatorIds(), " ")); // optional validator method if (!isBlank(validationAnnotation.validator())) { pathValidator.setValidatorExpression( new ConstantExpression(validationAnnotation.validator())); } // add PathValidator to the mapping mapping.getPathValidators().add(pathValidator); } // process converters for (URLConverter converterAnnotation : mappingAnnotation.converter()) { // index attribute is required in this case if (converterAnnotation.index() < 0) { throw new IllegalArgumentException( "Please set the index of the path parameter you want to convert with the @URLConverter specified on mapping: " + mapping.getId()); } // prepare PathValidator PathConverter pathConverter = new PathConverter(); pathConverter.setIndex(converterAnnotation.index()); pathConverter.setConverterId(StringUtils.trimToNull(converterAnnotation.converterId())); // add PathValidator to the mapping mapping.addPathConverter(pathConverter); } // return mapping id return mapping.getId().trim(); } /** * Checks the class for a {@link URLBeanName} annotation. * * @param clazz Class to scan */ private void processPrettyBeanAnnotation(final Class clazz) { // get reference to @URLMapping annotation URLBeanName prettyBean = (URLBeanName) clazz.getAnnotation(URLBeanName.class); // process annotation if it exists if (prettyBean != null) { // log class name if (log.isTraceEnabled()) { log.trace("Found @URLBeanName annotation on class: " + clazz.getName()); } // add bean to map beanNameMap.put(clazz, prettyBean.value()); } } /** * Searches for {@link URLAction} or {@link URLActions} annotations on a method. * * @param method Method to scan * @param classMappingIds The mapping IDs of the class this method belongs to */ private void processMethodAnnotations(final Method method, final String[] classMappingIds) { // is there a @URLAction annotation on the class? URLAction actionAnnotation = method.getAnnotation(URLAction.class); if (actionAnnotation != null) { processPrettyActionAnnotation(actionAnnotation, method, classMappingIds); } // is there a @URLAction container annotation on the class? URLActions actionsAnnotation = method.getAnnotation(URLActions.class); if (actionsAnnotation != null) { // process all @URLAction annotations for (URLAction child : actionsAnnotation.actions()) { processPrettyActionAnnotation(child, method, classMappingIds); } } } /** * Searches for {@link URLQueryParameter} annotations on a single field. * * @param field Field to scan * @param classMappingIds The mapping IDs of the class this method belongs to */ private void processFieldAnnotations(final Field field, final String[] classMappingIds) { // Is there a @URLQueryParameter annotation? URLQueryParameter queryParamAnnotation = field.getAnnotation(URLQueryParameter.class); if (queryParamAnnotation != null) { // create a QueryParamSpec from the annotation QueryParamSpec queryParam = new QueryParamSpec(); queryParam.setFieldName(field.getName()); queryParam.setOwnerClass(field.getDeclaringClass()); queryParam.setName(queryParamAnnotation.value()); queryParam.setOnPostback(queryParamAnnotation.onPostback()); // check which mapping the action belongs to if (!isBlank(queryParamAnnotation.mappingId())) { // action belongs to the mapping mentioned with mappingId attribute queryParam.setMappingIds(new String[] {queryParamAnnotation.mappingId().trim()}); } else if ((classMappingIds != null) && (classMappingIds.length > 0)) { // use the mappings found on the class queryParam.setMappingIds(classMappingIds); } else { throw new IllegalArgumentException( "Unable to find a suitable mapping " + "for the query-parameter definied on field '" + field.getName() + "' in class '" + field.getDeclaringClass().getName() + "'. Either place a @URLMapping annotation on the " + "class or reference a foreign mapping using the 'mappingId' attribute."); } // check if there is also a validation annotation placed on the field URLValidator validationAnnotation = field.getAnnotation(URLValidator.class); // check if annotation has been found if (validationAnnotation != null) { // set validation options on the QueryParamSpec object queryParam.setValidatorIds(validationAnnotation.validatorIds()); queryParam.setOnError(validationAnnotation.onError()); queryParam.setValidator(validationAnnotation.validator()); } // check if there is also a converter annotation placed on the field URLConverter converterAnnotation = field.getAnnotation(URLConverter.class); if (converterAnnotation != null) { queryParam.setConverterId(converterAnnotation.converterId().trim()); } // add the new spec object to the list of specs queryParamList.add(queryParam); } } /** * Creates a {@link UrlAction} object from the supplied {@link URLAction} annotation * * @param actionAnnotation The annotation * @param method The method that was annotated * @param classMappingIds the mapping IDs of the current class */ private void processPrettyActionAnnotation( final URLAction actionAnnotation, final Method method, final String[] classMappingIds) { // Create ActionSpec ActionSpec actionSpec = new ActionSpec(); actionSpec.setMethod(method); actionSpec.setOnPostback(actionAnnotation.onPostback()); actionSpec.setInheritable(actionAnnotation.inheritable()); actionSpec.setPhaseId(actionAnnotation.phaseId()); // check which mapping the action belongs to if (!isBlank(actionAnnotation.mappingId())) { // action belongs to the mapping mentioned with mappingId attribute actionSpec.setMappingIds(new String[] {actionAnnotation.mappingId().trim()}); } else if ((classMappingIds != null) && (classMappingIds.length > 0)) { // use the mapping found on the class actionSpec.setMappingIds(classMappingIds); } else { // No mapping found... throw an exception.. throw new IllegalArgumentException( "Unable to find a suitable mapping " + "for the action definied on method '" + method.getName() + "' in class '" + method.getDeclaringClass().getName() + "'. Either place a @URLMapping annotation on the " + "class or reference a foreign mapping using the 'mappingId' attribute."); } // add action to list of actions urlActions.add(actionSpec); } /** * Returns <code>true</code> for "blank" strings. * * @param str Input string * @return <code>true</code> if string is <code>null</code> or trimmed value is empty */ private static boolean isBlank(final String str) { return (str == null) || (str.trim().length() == 0); } /** * This methods adds all mappings found to the supplied {@link PrettyConfigBuilder}. It should be * called after all classes has been scanned via {@link #processClass(Class)}. * * @param builder The builder to add the mappings to */ public void build(final PrettyConfigBuilder builder) { // process all actions found for (ActionSpec actionSpec : urlActions) { // create an action for each referenced mapping for (String mappingId : actionSpec.getMappingIds()) { // Get the mapping references by the action UrlMapping mapping = urlMappings.get(mappingId); /* * Fail for unresolved mappings. This may happen when the user places * invalid mapping IDs in the mappingId attribute of * * @URLAction or @URLQueryParameter */ if (mapping == null) { throw new IllegalArgumentException( "Unable to find the mapping '" + mappingId + "' referenced at method '" + actionSpec.getMethod().getName() + "' in class '" + actionSpec.getMethod().getDeclaringClass().getName() + "'."); } // build UrlMapping UrlAction urlAction = new UrlAction(); urlAction.setPhaseId(actionSpec.getPhaseId()); urlAction.setOnPostback(actionSpec.isOnPostback()); urlAction.setInheritable(actionSpec.isInheritable()); // try to get bean name Class clazz = actionSpec.getMethod().getDeclaringClass(); // build expression PrettyExpression expression = buildPrettyExpression(clazz, actionSpec.getMethod().getName()); urlAction.setAction(expression); // trace if (log.isTraceEnabled()) { log.trace( "Adding action expression '" + urlAction.getAction() + "' to mapping: " + mapping.getId()); } // register this action mapping.addAction(urlAction); } } for (QueryParamSpec queryParamSpec : queryParamList) { // create a query param for each referenced mapping for (String mappingId : queryParamSpec.getMappingIds()) { // Get the mapping references by the query param UrlMapping mapping = urlMappings.get(mappingId); // fail for unresolved mappings if (mapping == null) { throw new IllegalArgumentException( "Unable to find the mapping '" + mappingId + "' referenced at field '" + queryParamSpec.getFieldName() + "' in class '" + queryParamSpec.getOwnerClass().getName() + "'."); } // build UrlMapping QueryParameter queryParam = new QueryParameter(); queryParam.setName(queryParamSpec.getName()); queryParam.setOnError(queryParamSpec.getOnError()); queryParam.setConverterId(StringUtils.trimToNull(queryParamSpec.getConverterId())); queryParam.setValidatorIds(join(queryParamSpec.getValidatorIds(), " ")); queryParam.setOnPostback(queryParamSpec.isOnPostback()); // optional validator method if (!isBlank(queryParamSpec.getValidator())) { queryParam.setValidatorExpression(new ConstantExpression(queryParamSpec.getValidator())); } // try to get bean name Class<?> clazz = queryParamSpec.getOwnerClass(); // build expression PrettyExpression expression = buildPrettyExpression(clazz, queryParamSpec.getFieldName()); queryParam.setExpression(expression); // trace if (log.isTraceEnabled()) { log.trace( "Registered query-param '" + queryParam.getName() + "' to '" + expression + "' in mapping: " + mapping.getId()); } // register this action mapping.addQueryParam(queryParam); } } // finally register all mappings for (UrlMapping mapping : urlMappings.values()) { builder.addMapping(mapping); } } /** * Creates a {@link PrettyExpression} for a class and component. This method may return a {@link * ConstantExpression} or a {@link LazyExpression}. * * @param clazz The class of the bean * @param component the component (property or method name) * @return The expression */ private PrettyExpression buildPrettyExpression(final Class<?> clazz, final String component) { if (log.isTraceEnabled()) { log.trace("Searching name of bean: " + clazz.getName()); } // get name from internal map build from @URLBeanName annotations and // previously resolved names String beanName = beanNameMap.get(clazz); // return a constant expression if (beanName != null) { if (log.isTraceEnabled()) { log.trace("Got bean name from @URLBeanName annotation: " + beanName); } return new ConstantExpression("#{" + beanName + "." + component + "}"); } // build a lazy expression else { if (log.isTraceEnabled()) { log.trace("Name of bean not found. Building lazy expression for: " + clazz.getName()); } return new LazyExpression(beanNameFinder, clazz, component); } } /** * Joins the list of values. * * @param values values to join * @param separator the separator to use * @return joined list of values */ private static String join(final String[] values, final String separator) { StringBuilder result = new StringBuilder(); if (values != null) { for (int i = 0; i < values.length; i++) { if (i > 0) { result.append(separator); } result.append(values[i]); } } return result.toString(); } /** * Internal class to hold parameters of a {@link URLAction} annotation. * * @author Christian Kaltepoth */ private static class ActionSpec { private Method method; private boolean onPostback; private PhaseId phaseId; private String[] mappingIds; private boolean inheritable; public boolean isOnPostback() { return onPostback; } public void setOnPostback(final boolean onPostback) { this.onPostback = onPostback; } public PhaseId getPhaseId() { return phaseId; } public void setPhaseId(final PhaseId phaseId) { this.phaseId = phaseId; } public Method getMethod() { return method; } public void setMethod(final Method method) { this.method = method; } public String[] getMappingIds() { return mappingIds; } public void setMappingIds(final String[] mappingIds) { this.mappingIds = mappingIds; } public boolean isInheritable() { return inheritable; } public void setInheritable(boolean inheritable) { this.inheritable = inheritable; } } /** * Internal class to hold parameters of a Pretty annotation. * * @author Christian Kaltepoth */ private static class QueryParamSpec { private String fieldName; private Class<?> ownerClass; private String[] mappingIds; private String name; private String onError; private String converterId; private String[] validatorIds = {}; private String validator; private boolean onPostback; public String getValidator() { return validator; } public void setValidator(final String validator) { this.validator = validator; } public String getName() { return name; } public void setName(final String name) { this.name = name; } public String getFieldName() { return fieldName; } public void setFieldName(final String fieldName) { this.fieldName = fieldName; } public Class<?> getOwnerClass() { return ownerClass; } public void setOwnerClass(final Class<?> ownerClass) { this.ownerClass = ownerClass; } public String getOnError() { return onError; } public void setOnError(final String onError) { this.onError = onError; } public String[] getValidatorIds() { return validatorIds; } public void setValidatorIds(final String[] validatorIds) { this.validatorIds = validatorIds; } public boolean isOnPostback() { return onPostback; } public void setOnPostback(final boolean onPostback) { this.onPostback = onPostback; } public String[] getMappingIds() { return mappingIds; } public void setMappingIds(final String[] mappingIds) { this.mappingIds = mappingIds; } public String getConverterId() { return converterId; } public void setConverterId(String converterId) { this.converterId = converterId; } } }
/** * This methods adds all mappings found to the supplied {@link PrettyConfigBuilder}. It should be * called after all classes has been scanned via {@link #processClass(Class)}. * * @param builder The builder to add the mappings to */ public void build(final PrettyConfigBuilder builder) { // process all actions found for (ActionSpec actionSpec : urlActions) { // create an action for each referenced mapping for (String mappingId : actionSpec.getMappingIds()) { // Get the mapping references by the action UrlMapping mapping = urlMappings.get(mappingId); /* * Fail for unresolved mappings. This may happen when the user places * invalid mapping IDs in the mappingId attribute of * * @URLAction or @URLQueryParameter */ if (mapping == null) { throw new IllegalArgumentException( "Unable to find the mapping '" + mappingId + "' referenced at method '" + actionSpec.getMethod().getName() + "' in class '" + actionSpec.getMethod().getDeclaringClass().getName() + "'."); } // build UrlMapping UrlAction urlAction = new UrlAction(); urlAction.setPhaseId(actionSpec.getPhaseId()); urlAction.setOnPostback(actionSpec.isOnPostback()); urlAction.setInheritable(actionSpec.isInheritable()); // try to get bean name Class clazz = actionSpec.getMethod().getDeclaringClass(); // build expression PrettyExpression expression = buildPrettyExpression(clazz, actionSpec.getMethod().getName()); urlAction.setAction(expression); // trace if (log.isTraceEnabled()) { log.trace( "Adding action expression '" + urlAction.getAction() + "' to mapping: " + mapping.getId()); } // register this action mapping.addAction(urlAction); } } for (QueryParamSpec queryParamSpec : queryParamList) { // create a query param for each referenced mapping for (String mappingId : queryParamSpec.getMappingIds()) { // Get the mapping references by the query param UrlMapping mapping = urlMappings.get(mappingId); // fail for unresolved mappings if (mapping == null) { throw new IllegalArgumentException( "Unable to find the mapping '" + mappingId + "' referenced at field '" + queryParamSpec.getFieldName() + "' in class '" + queryParamSpec.getOwnerClass().getName() + "'."); } // build UrlMapping QueryParameter queryParam = new QueryParameter(); queryParam.setName(queryParamSpec.getName()); queryParam.setOnError(queryParamSpec.getOnError()); queryParam.setConverterId(StringUtils.trimToNull(queryParamSpec.getConverterId())); queryParam.setValidatorIds(join(queryParamSpec.getValidatorIds(), " ")); queryParam.setOnPostback(queryParamSpec.isOnPostback()); // optional validator method if (!isBlank(queryParamSpec.getValidator())) { queryParam.setValidatorExpression(new ConstantExpression(queryParamSpec.getValidator())); } // try to get bean name Class<?> clazz = queryParamSpec.getOwnerClass(); // build expression PrettyExpression expression = buildPrettyExpression(clazz, queryParamSpec.getFieldName()); queryParam.setExpression(expression); // trace if (log.isTraceEnabled()) { log.trace( "Registered query-param '" + queryParam.getName() + "' to '" + expression + "' in mapping: " + mapping.getId()); } // register this action mapping.addQueryParam(queryParam); } } // finally register all mappings for (UrlMapping mapping : urlMappings.values()) { builder.addMapping(mapping); } }
/** * Process a single {@link URLMapping} annotation. * * @param clazz The class that the annotation was found on * @param mappingAnnotation The annotation to process * @return The mapping ID of the mapping found */ private String processPrettyMappingAnnotation( final Class clazz, final URLMapping mappingAnnotation) { // log class name if (log.isTraceEnabled()) { log.trace("Found @URLMapping annotation on class: " + clazz.getName()); } // create UrlMapping from annotation UrlMapping mapping = new UrlMapping(); mapping.setId(mappingAnnotation.id()); mapping.setParentId(mappingAnnotation.parentId()); mapping.setPattern(mappingAnnotation.pattern()); mapping.setViewId(mappingAnnotation.viewId()); mapping.setOutbound(mappingAnnotation.outbound()); mapping.setOnPostback(mappingAnnotation.onPostback()); // register mapping Object existingMapping = urlMappings.put(mapping.getId(), mapping); // fail if a mapping with this ID already existed if (existingMapping != null) { throw new IllegalArgumentException("Duplicated mapping id: " + mapping.getId()); } // At bean name to lookup map if it has been specified if ((mappingAnnotation.beanName() != null) && (mappingAnnotation.beanName().length() > 0)) { beanNameMap.put(clazz, mappingAnnotation.beanName()); } // process validations for (URLValidator validationAnnotation : mappingAnnotation.validation()) { // index attribute is required in this case if (validationAnnotation.index() < 0) { throw new IllegalArgumentException( "Please set the index of the path parameter you want to validate with the @URLValidator specified on mapping: " + mapping.getId()); } // prepare PathValidator PathValidator pathValidator = new PathValidator(); pathValidator.setIndex(validationAnnotation.index()); pathValidator.setOnError(validationAnnotation.onError()); pathValidator.setValidatorIds(join(validationAnnotation.validatorIds(), " ")); // optional validator method if (!isBlank(validationAnnotation.validator())) { pathValidator.setValidatorExpression( new ConstantExpression(validationAnnotation.validator())); } // add PathValidator to the mapping mapping.getPathValidators().add(pathValidator); } // process converters for (URLConverter converterAnnotation : mappingAnnotation.converter()) { // index attribute is required in this case if (converterAnnotation.index() < 0) { throw new IllegalArgumentException( "Please set the index of the path parameter you want to convert with the @URLConverter specified on mapping: " + mapping.getId()); } // prepare PathValidator PathConverter pathConverter = new PathConverter(); pathConverter.setIndex(converterAnnotation.index()); pathConverter.setConverterId(StringUtils.trimToNull(converterAnnotation.converterId())); // add PathValidator to the mapping mapping.addPathConverter(pathConverter); } // return mapping id return mapping.getId().trim(); }