// Copyright 2015 ThoughtWorks, Inc. // This file is part of Gauge-Java. // This program is free software. // // It is dual-licensed under: // 1) the GNU General Public License as published by the Free Software Foundation, // either version 3 of the License, or (at your option) any later version; // or // 2) the Eclipse Public License v1.0. // // You can redistribute it and/or modify it under the terms of either license. // We would then provide copied of each license in a separate .txt file with the name of the license as the title of the file. package com.thoughtworks.gauge.registry; import com.thoughtworks.gauge.AfterClassSteps; import com.thoughtworks.gauge.AfterScenario; import com.thoughtworks.gauge.AfterSpec; import com.thoughtworks.gauge.AfterStep; import com.thoughtworks.gauge.AfterSuite; import com.thoughtworks.gauge.BeforeClassSteps; import com.thoughtworks.gauge.BeforeScenario; import com.thoughtworks.gauge.BeforeSpec; import com.thoughtworks.gauge.BeforeStep; import com.thoughtworks.gauge.BeforeSuite; import com.thoughtworks.gauge.Operator; import com.thoughtworks.gauge.hook.Hook; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.ArrayList; import java.util.Collections; import java.util.Set; public class HooksRegistry { // Names of methods defined in each Hook annotation. Do not rename these methods in any Hook Class. public static final String TAGS_METHOD = "tags"; public static final String TAG_AGGREGATION_METHOD = "tagAggregation"; private static HashMap<Class, HashSet<Hook>> registryMap = new HashMap<Class, HashSet<Hook>>(); public static List<Hook> getBeforeSpecHooks() { return sort(registryMap.get(BeforeSpec.class)); } public static void addBeforeSpecHooks(Set<Method> methods) { addHooksWithTags(methods, BeforeSpec.class); } public static List<Hook> getAfterSpecHooks() { return sortReverse(registryMap.get(AfterSpec.class)); } public static void addAfterSpecHooks(Set<Method> methods) { addHooksWithTags(methods, AfterSpec.class); } public static List<Hook> getBeforeScenarioHooks() { return sort(registryMap.get(BeforeScenario.class)); } private static List<Hook> sort(Set<Hook> hooks) { List<Hook> hooksList = new ArrayList<Hook>(hooks); Collections.sort(hooksList); return hooksList; } private static List<Hook> sortReverse(Set<Hook> hooks) { List<Hook> hooksList = sort(hooks); Collections.reverse(hooksList); return hooksList; } public static void addBeforeScenarioHooks(Set<Method> methods) { addHooksWithTags(methods, BeforeScenario.class); } public static List<Hook> getAfterScenarioHooks() { return sortReverse(registryMap.get(AfterScenario.class)); } public static void addAfterScenarioHooks(Set<Method> methods) { addHooksWithTags(methods, AfterScenario.class); } public static List<Hook> getBeforeStepHooks() { return sort(registryMap.get(BeforeStep.class)); } public static void addBeforeStepHooks(Set<Method> methods) { addHooksWithTags(methods, BeforeStep.class); } public static List<Hook> getAfterStepHooks() { return sortReverse(registryMap.get(AfterStep.class)); } public static void setAfterStepHooks(Set<Method> methods) { addHooksWithTags(methods, AfterStep.class); } public static List<Hook> getBeforeSuiteHooks() { return sort(registryMap.get(BeforeSuite.class)); } public static void addBeforeSuiteHooks(Set<Method> methods) { addHooks(methods, BeforeSuite.class); } public static List<Hook> getAfterSuiteHooks() { return sortReverse(registryMap.get(AfterSuite.class)); } public static void addAfterSuiteHooks(Set<Method> methods) { addHooks(methods, AfterSuite.class); } public static void addAfterClassStepsHooks(Set<Method> methods) { addHooksWithTags(methods, AfterClassSteps.class); } public static void addBeforeClassStepsHooks(Set<Method> methods) { addHooksWithTags(methods, BeforeClassSteps.class); } public static List<Hook> getBeforeClassStepsHooksOfClass(Class<?> aClass) { return sort(findClassHooksForClass(getBeforeClassHooks(), aClass)); } public static List<Hook> getAfterClassStepsHooksOfClass(Class<?> aClass) { return sortReverse(findClassHooksForClass(getAfterClassHooks(), aClass)); } private static Set<Hook> findClassHooksForClass(List<Hook> allClassHooks, Class<?> aClass) { HashSet<Hook> matchingClassHooks = new HashSet<Hook>(); for (Hook classStepsHook : allClassHooks) { if (classStepsHook.getMethod().getDeclaringClass().equals(aClass)) { matchingClassHooks.add(classStepsHook); } } return matchingClassHooks; } private static void addHooks(Set<Method> methods, Class hookClass) { if (!registryMap.containsKey(hookClass)) { registryMap.put(hookClass, new HashSet<Hook>()); } for (Method method : methods) { registryMap.get(hookClass).add(new Hook(method)); } } private static void addHooksWithTags(Set<Method> methods, Class hookClass) { if (!registryMap.containsKey(hookClass)) { registryMap.put(hookClass, new HashSet<Hook>()); } for (Method method : methods) { Annotation annotation = method.getAnnotation(hookClass); try { //Hack: Invoking methods on the annotation to avoid repeating logic. There is no hierarchy possible in annotations String[] tags = (String[]) annotation.getClass().getMethod(TAGS_METHOD).invoke(annotation); Operator tagsAggregation = (Operator) annotation.getClass().getMethod(TAG_AGGREGATION_METHOD).invoke(annotation); registryMap.get(hookClass).add(new Hook(method, tags, tagsAggregation)); } catch (Exception e) { e.printStackTrace(); continue; } } } private static List<Hook> getBeforeClassHooks() { return sort(registryMap.get(BeforeClassSteps.class)); } private static List<Hook> getAfterClassHooks() { return sortReverse(registryMap.get(AfterClassSteps.class)); } static void remove(Class hookType) { registryMap.remove(hookType); } }