protected static Set<ConfigurationMetadataHint> processHints(final Collection<ConfigurationMetadataProperty> props,
                                                                 final Collection<ConfigurationMetadataProperty> groups) {

        var hints = new LinkedHashSet<ConfigurationMetadataHint>();
        val allValidProps = props.stream()
            .filter(p -> p.getDeprecation() == null
                || Deprecation.Level.ERROR != p.getDeprecation().getLevel()).toList();

        for (val entry : allValidProps) {
            val propName = StringUtils.substringAfterLast(entry.getName(), ".");
            val groupName = StringUtils.substringBeforeLast(entry.getName(), ".");
            groups
                .stream()
                .filter(g -> g.getName().equalsIgnoreCase(groupName))
                .findFirst()
                .ifPresent(grp -> {
                    try {
                        val matcher = PATTERN_GENERICS.matcher(grp.getType());
                        val className = matcher.find() ? matcher.group(1) : grp.getType();
                        val clazz = ClassUtils.getClass(className);

                        val hint = new ConfigurationMetadataHint();
                        hint.setName(entry.getName());

                        val annotation = Arrays.stream(clazz.getAnnotations())
                            .filter(a -> a.annotationType().equals(RequiresModule.class))
                            .findFirst()
                            .map(RequiresModule.class::cast)
                            .orElseThrow(() -> new RuntimeException(clazz.getCanonicalName() + " is missing @RequiresModule"));

                        val valueHint = new ValueHint();

                        val hintsMap = new TreeMap<>();
                        hintsMap.put("module", annotation.name());
                        hintsMap.put("automated", annotation.automated());
                        valueHint.setValue(toJson(hintsMap));
                        valueHint.setDescription(RequiresModule.class.getName());
                        hint.getValues().add(valueHint);

                        val grpHint = new ValueHint();
                        grpHint.setValue(toJson(Map.of("owner", clazz.getCanonicalName())));
                        grpHint.setDescription(PropertyOwner.class.getName());
                        hint.getValues().add(grpHint);

                        val names = RelaxedPropertyNames.forCamelCase(propName);
                        names.getValues().forEach(Unchecked.consumer(name -> {
                            val f = ReflectionUtils.findField(clazz, name);
                            if (f != null && f.isAnnotationPresent(RequiredProperty.class)) {
                                val propertyHint = new ValueHint();
                                propertyHint.setValue(toJson(Map.of("owner", clazz.getName())));
                                propertyHint.setDescription(RequiredProperty.class.getName());
                                hint.getValues().add(propertyHint);
                            }
                            if (f != null && f.isAnnotationPresent(DurationCapable.class)) {
                                val propertyHint = new ValueHint();
                                propertyHint.setDescription(DurationCapable.class.getName());
                                propertyHint.setValue(toJson(List.of(DurationCapable.class.getName())));
                                hint.getValues().add(propertyHint);
                            }

                            if (f != null && f.isAnnotationPresent(ExpressionLanguageCapable.class)) {
                                val propertyHint = new ValueHint();
                                propertyHint.setDescription(ExpressionLanguageCapable.class.getName());
                                propertyHint.setValue(toJson(List.of(ExpressionLanguageCapable.class.getName())));
                                hint.getValues().add(propertyHint);
                            }

                            if (f != null && f.isAnnotationPresent(RegularExpressionCapable.class)) {
                                val propertyHint = new ValueHint();
                                propertyHint.setDescription(RegularExpressionCapable.class.getName());
                                propertyHint.setValue(toJson(List.of(RegularExpressionCapable.class.getName())));
                                hint.getValues().add(propertyHint);
                            }
                        }));

                        if (!hint.getValues().isEmpty()) {
                            hints.add(hint);
                        }
                    } catch (final Exception e) {
                        LOGGER.error(e.getMessage(), e);
                    }
                });
        }
        return hints;
    }
Main.

@param args the args
@throws Exception the exception
api/cas-server-core-api-configuration-model/src/main/java/org/apereo/cas/configuration/metadata/ConfigurationMetadataGenerator.java