# BuiltInLintingRulesFactory.java

    package org.thewonderlemming.c4plantuml.mojo.linting.rules.builtin;

    import java.lang.reflect.InvocationTargetException;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Optional;
    import java.util.stream.Stream;

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.thewonderlemming.c4plantuml.linter.rules.AbstractLintingRule;
    import org.thewonderlemming.c4plantuml.linter.rules.RuleParameters;
    import org.thewonderlemming.c4plantuml.linter.rules.builtin.AliasesShouldBeListedInDictionaryRule;
    import org.thewonderlemming.c4plantuml.linter.rules.builtin.AliasesShouldBeUniqueRule;
    import org.thewonderlemming.c4plantuml.linter.rules.builtin.AliasesShouldFollowStandardConventionRule;
    import org.thewonderlemming.c4plantuml.linter.rules.builtin.NoDuplicateRelationshipsRule;
    import org.thewonderlemming.c4plantuml.linter.rules.builtin.NoOrphanAliasInBoundariesRule;
    import org.thewonderlemming.c4plantuml.linter.rules.builtin.NoOrphanAliasInLayoutsRule;
    import org.thewonderlemming.c4plantuml.linter.rules.builtin.NoOrphanAliasInRelationshipsRule;
    import org.thewonderlemming.c4plantuml.mojo.linting.BuiltInRules;
    import org.thewonderlemming.c4plantuml.mojo.linting.rules.builtin.parameters.AliasesShouldBeListedInDictionaryRuleParametersFactory;
    import org.thewonderlemming.c4plantuml.mojo.linting.rules.builtin.parameters.AliasesShouldBeUniqueRuleParametersFactory;
    import org.thewonderlemming.c4plantuml.mojo.linting.rules.builtin.parameters.AliasesShouldFollowStandardConventionRuleParametersFactory;
    import org.thewonderlemming.c4plantuml.mojo.linting.rules.builtin.parameters.NoDuplicateRelationshipsRuleParametersFactory;
    import org.thewonderlemming.c4plantuml.mojo.linting.rules.builtin.parameters.NoOrphanAliasInBoundariesRuleParametersFactory;
    import org.thewonderlemming.c4plantuml.mojo.linting.rules.builtin.parameters.NoOrphanAliasInLayoutsRuleParametersFactory;
    import org.thewonderlemming.c4plantuml.mojo.linting.rules.builtin.parameters.NoOrphanAliasInRelationshipsRuleParametersFactory;

    /**
     * Maps {@link AbstractLintingRule} classes to their {@link AbstractRuleParametersFactory} and their name.
     *
     * @author thewonderlemming
     *
     */
    public enum BuiltInLintingRulesFactory {

        /**
         * The {@link AliasesShouldBeListedInDictionaryRule} mapping.
         */
        ALIASES_SHOULD_BE_LISTED_IN_DICTIONARY(
            "aliasesShouldBeListedInDictionary",
            AliasesShouldBeListedInDictionaryRule.class,
            AliasesShouldBeListedInDictionaryRuleParametersFactory.class),

        /**
         * The {@link AliasesShouldBeUniqueRule} mapping.
         */
        ALIASES_SHOULD_BE_UNIQUE(
            "aliasesShouldBeUnique",
            AliasesShouldBeUniqueRule.class,
            AliasesShouldBeUniqueRuleParametersFactory.class),

        /**
         * The {@link AliasesShouldFollowStandardConventionRule} mapping.
         */
        ALIASES_SHOULD_FOLLOW_STANDARD_CONVENTION(
            "aliasesShouldFollowStandardConvention",
            AliasesShouldFollowStandardConventionRule.class,
            AliasesShouldFollowStandardConventionRuleParametersFactory.class),

        /**
         * The {@link NoDuplicateRelationshipsRule} mapping.
         */
        NO_DUPLICATES_IN_RELATIONSHIPS(
            "noDuplicateRelationships",
            NoDuplicateRelationshipsRule.class,
            NoDuplicateRelationshipsRuleParametersFactory.class),

        /**
         * The {@link NoOrphanAliasInBoundariesRule} mapping.
         */
        NO_ORPHAN_ALIAS_IN_BOUNDARIES(
            "noOrphanAliasInBoundaries",
            NoOrphanAliasInBoundariesRule.class,
            NoOrphanAliasInBoundariesRuleParametersFactory.class),

        /**
         * The {@link NoOrphanAliasInLayoutsRule} mapping.
         */
        NO_ORPHAN_ALIAS_IN_LAYOUTS(
            "noOrphanAliasInLayouts",
            NoOrphanAliasInLayoutsRule.class,
            NoOrphanAliasInLayoutsRuleParametersFactory.class),

        /**
         * The {@link NoOrphanAliasInRelationshipsRule} mapping.
         */
        NO_ORPHAN_ALIAS_IN_RELATIONSHIPS(
            "noOrphanAliasInRelationships",
            NoOrphanAliasInRelationshipsRule.class,
            NoOrphanAliasInRelationshipsRuleParametersFactory.class);

        private static final Logger LOGGER = LoggerFactory.getLogger(BuiltInLintingRulesFactory.class);

        private static final Map<String, BuiltInLintingRulesFactory> LOOKUP_BY_NAME = new HashMap<>();

        private static final Map<Class<? extends AbstractLintingRule>, BuiltInLintingRulesFactory> LOOKUP_BY_TYPE = new HashMap<>();

        private final String ruleName;

        private final Class<? extends AbstractRuleParametersFactory> ruleParametersFactoryType;

        private final Class<? extends AbstractLintingRule> ruleType;

        static {

            Stream
                .of(BuiltInLintingRulesFactory.values())
                    .forEach(value -> {
                        LOOKUP_BY_NAME.put(value.ruleName, value);
                        LOOKUP_BY_TYPE.put(value.getRuleType(), value);
                    });
        }

        /**
         * Builds a {@link AbstractLintingRule} instance, given its name and the {@link BuiltInRules} parameters POJO.
         *
         * @param ruleName the name of the rule to build.
         * @param parameters the {@code pom.xml} configuration.
         * @return an {@link Optional} of the built rule on success, or empty else.
         */
        public static Optional<AbstractLintingRule> createInstanceForName(final String ruleName,
            final BuiltInRules parameters) {

            final BuiltInLintingRulesFactory rule = LOOKUP_BY_NAME.get(ruleName);

            if (null != rule) {

                try {

                    final AbstractRuleParametersFactory config = rule.ruleParametersFactoryType
                        .getDeclaredConstructor(BuiltInRules.class)
                            .newInstance(parameters);

                    LOGGER
                        .debug("Linting rule {} is activated: {}",
                            ruleName,
                            config.isActivated());

                    return config.isActivated()
                        ? Optional.ofNullable(rule.createInstance(config.getRuleParameters()))
                        : Optional.empty();

                } catch (
                    InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
                    | NoSuchMethodException | SecurityException e) {

                    LOGGER
                        .error("Cannot create instance of rule named '{}' because of exception: {}",
                            ruleName,
                            e.getMessage(),
                            e);
                }
            }

            return Optional.empty();
        }

        /**
         * Returns the mapped name of a {@link AbstractLintingRule}, given its type.
         *
         * @param ruleType the type of the rule to retrieve the name from.
         * @return an {@link Optional} of the name of the rule if found, empty else.
         */
        public static Optional<String> getRuleNameForType(final Class<? extends AbstractLintingRule> ruleType) {

            return LOOKUP_BY_TYPE.containsKey(ruleType)
                ? Optional.ofNullable(LOOKUP_BY_TYPE.get(ruleType).getRuleName())
                : Optional.empty();
        }

        /**
         * Tells whether or not the given {@link AbstractLintingRule} is a built-in rule, or a custom one, which requires
         * a different building process.
         *
         * @param lintingRuleType the type of the rule to check for.
         * @return {@code true} if the rule is a built-in one, {@code false} else.
         */
        public static boolean isBuiltInLintingRule(final Class<? extends AbstractLintingRule> lintingRuleType) {
            return LOOKUP_BY_TYPE.containsKey(lintingRuleType);
        }

        private BuiltInLintingRulesFactory(final String ruleName, final Class<? extends AbstractLintingRule> ruleType,
            final Class<? extends AbstractRuleParametersFactory> ruleParametersFactoryType) {

            this.ruleName = ruleName;
            this.ruleType = ruleType;
            this.ruleParametersFactoryType = ruleParametersFactoryType;
        }

        /**
         * Returns the name of the current mapping.
         *
         * @return the name of the mapping.
         */
        public String getRuleName() {
            return this.ruleName;
        }

        /**
         * Returns the {@link AbstractLintingRule} type of the current mapping.
         *
         * @return the type of the current mapping.
         */
        public Class<? extends AbstractLintingRule> getRuleType() {
            return this.ruleType;
        }

        private AbstractLintingRule createInstance(final RuleParameters parameters)
            throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException,
            NoSuchMethodException, SecurityException {

            return this.ruleType.getConstructor(RuleParameters.class).newInstance(parameters);
        }
    }