class AbstractRuleDefinitionReader implements RuleDefinitionReader {

    public List<RuleDefinition> read(Reader reader) throws Exception {
        List<RuleDefinition> ruleDefinitions = new ArrayList<>();
        Iterable<Map<String, Object>> rules = loadRules(reader);
        for (Map<String, Object> rule : rules) {
            ruleDefinitions.add(createRuleDefinition(rule));
        }
        return ruleDefinitions;
    }

    /**
     * Load rules from the given reader as an iterable of Maps.
     *
     * @param reader to read rules from
     * @return an iterable of rule Maps
     * @throws Exception if unable to load rules
     */
    protected abstract Iterable<Map<String, Object>> loadRules(Reader reader) throws Exception;

    /**
     * Create a rule definition.
     *
     * @param map of rule properties
     * @return a rule definition
     */
    protected RuleDefinition createRuleDefinition(Map<String, Object> map) {<FILL_FUNCTION_BODY>}
}

        RuleDefinition ruleDefinition = new RuleDefinition();

        String name = (String) map.get("name");
        ruleDefinition.setName(name != null ? name : Rule.DEFAULT_NAME);

        String description = (String) map.get("description");
        ruleDefinition.setDescription(description != null ? description : Rule.DEFAULT_DESCRIPTION);

        Integer priority = (Integer) map.get("priority");
        ruleDefinition.setPriority(priority != null ? priority : Rule.DEFAULT_PRIORITY);

        String compositeRuleType = (String) map.get("compositeRuleType");

        String condition = (String) map.get("condition");
        if (condition == null && compositeRuleType == null) {
            throw new IllegalArgumentException("The rule condition must be specified");
        }
        ruleDefinition.setCondition(condition);

        List<String> actions = (List<String>) map.get("actions");
        if ((actions == null || actions.isEmpty()) && compositeRuleType == null) {
            throw new IllegalArgumentException("The rule action(s) must be specified");
        }
        ruleDefinition.setActions(actions);

        List<Object> composingRules = (List<Object>) map.get("composingRules");
        if ((composingRules != null && !composingRules.isEmpty()) && compositeRuleType == null) {
            throw new IllegalArgumentException("Non-composite rules cannot have composing rules");
        } else if ((composingRules == null || composingRules.isEmpty()) && compositeRuleType != null) {
            throw new IllegalArgumentException("Composite rules must have composing rules specified");
        } else if (composingRules != null) {
            List<RuleDefinition> composingRuleDefinitions = new ArrayList<>();
            for (Object rule : composingRules) {
                Map<String, Object> composingRuleMap = (Map<String, Object>) rule;
                composingRuleDefinitions.add(createRuleDefinition(composingRuleMap));
            }
            ruleDefinition.setComposingRules(composingRuleDefinitions);
            ruleDefinition.setCompositeRuleType(compositeRuleType);
        }

        return ruleDefinition;
    
<no_super_class>