View Javadoc

1   /*
2    * Copyright 2010-2013 smartics, Kronseder & Reiner GmbH
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package de.smartics.testdoc.report.export.doc;
17  
18  import java.util.ArrayList;
19  import java.util.Arrays;
20  import java.util.Collections;
21  import java.util.List;
22  
23  import de.smartics.testdoc.core.doc.ScenarioTestDoc;
24  
25  /**
26   * Filter on test cases based on categories.
27   */
28  public class SelectionFilter
29  {
30    // ********************************* Fields *********************************
31  
32    // --- constants ------------------------------------------------------------
33  
34    /**
35     * Constant to define a filter to include or exclude everything.
36     * <p>
37     * The value of this constant is {@value}.
38     * </p>
39     */
40    public static final String ALL_FILTER = "ALL";
41  
42    /**
43     * Standard filter that does include everything and exclude nothing.
44     */
45    public static final SelectionFilter STANDARD = new SelectionFilter();
46  
47    /**
48     * Used to signal that everything that is not included, is excluded.
49     */
50    private static final List<Class<?>> ALL = null;
51  
52    // --- members --------------------------------------------------------------
53  
54    /**
55     * The category names of test scenarios that should be included. If a scenario
56     * is tagged by at least one of the mentioned filters, it will be displayed in
57     * the report.
58     * <p>
59     * The category is specified as a JUnit <code>Category</code>.
60     * </p>
61     * <p>
62     * Per default, if no filter category is specified, all categories are
63     * included.
64     * </p>
65     */
66    private final List<Class<?>> includeCategories;
67  
68    /**
69     * The category names of test scenarios that should be excluded. If a scenario
70     * is tagged by at least one of the mentioned filters, it will not be
71     * displayed in the report.
72     * <p>
73     * The category is specified as a JUnit <code>Category</code>.
74     * </p>
75     * <p>
76     * Per default, if no filter category is specified, categories deriving from
77     * {@link de.smartics.testdoc.categories.Technical} will not be rendered.
78     * </p>
79     */
80    private final List<Class<?>> excludeCategories;
81  
82    // ****************************** Initializer *******************************
83  
84    // ****************************** Constructors ******************************
85  
86    /**
87     * Constructor for the singleton provided by this class.
88     */
89    private SelectionFilter()
90    {
91      this(Arrays.asList(ALL_FILTER), new ArrayList<String>());
92    }
93  
94    /**
95     * Default constructor.
96     *
97     * @param includeCategories the category names of test scenarios that should
98     *          be included.
99     * @param excludeCategories the category names of test scenarios that should
100    *          be excluded.
101    */
102   public SelectionFilter(final List<String> includeCategories,
103       final List<String> excludeCategories)
104   {
105     this.includeCategories = provideCategories(includeCategories);
106     this.excludeCategories = provideCategories(excludeCategories);
107   }
108 
109   // ****************************** Inner Classes *****************************
110 
111   // ********************************* Methods ********************************
112 
113   // --- init -----------------------------------------------------------------
114 
115   // --- get&set --------------------------------------------------------------
116 
117   // --- business -------------------------------------------------------------
118 
119   /**
120    * Filters scenarios by their categories.
121    *
122    * @param scenarios the scenarios to filter.
123    * @return the filtered scenarios.
124    */
125   public List<ScenarioTestDoc> filterScenarios(
126       final List<ScenarioTestDoc> scenarios)
127   {
128     final List<ScenarioTestDoc> filtered =
129         new ArrayList<ScenarioTestDoc>(scenarios.size());
130     for (final ScenarioTestDoc scenario : scenarios)
131     {
132       final List<String> categories = scenario.getCategories();
133       if (acceptsAllOf(categories))
134       {
135         filtered.add(scenario);
136       }
137     }
138     return filtered;
139   }
140 
141   /**
142    * Checks if the scenario is accepted.
143    *
144    * @param scenario the scenario to check.
145    * @return true when accepted false otherwise.
146    */
147   public boolean acceptScenario(final ScenarioTestDoc scenario)
148   {
149     return acceptsAllOf(scenario.getCategories());
150   }
151 
152   /**
153    * Checks if all categories are accepted.
154    *
155    * @param categories the categories to be accepted.
156    * @return <code>true</code> if all categories are accepted,
157    *         <code>false</code> if at least one category is rejected.
158    */
159   private boolean acceptsAllOf(final List<String> categories)
160   {
161     if (categories.isEmpty())
162     {
163       return excludeCategories != ALL; // NOPMD
164     }
165 
166     for (final String category : categories)
167     {
168       if (!acceptCategory(category))
169       {
170         return false;
171       }
172     }
173 
174     return true;
175   }
176 
177   /**
178    * If there are no filter elements, a default filter is created.
179    */
180   private static List<Class<?>> provideCategories(
181       final List<String> filterCategories) throws IllegalArgumentException
182   {
183     final List<Class<?>> categories = new ArrayList<Class<?>>();
184     if (filterCategories != null)
185     {
186       for (final String categoryName : filterCategories)
187       {
188         if (ALL_FILTER.equals(categoryName))
189         {
190           return ALL;
191         }
192         try
193         {
194           final Class<?> category = Class.forName(categoryName);
195           categories.add(category);
196         }
197         catch (final ClassNotFoundException e)
198         {
199           throw new IllegalArgumentException("Unknown category '"
200                                              + categoryName + "'.", e);
201         }
202       }
203     }
204     return Collections.unmodifiableList(categories);
205   }
206 
207   /**
208    * Checks whether or not the category is accepted to display the so tagged
209    * scenario.
210    *
211    * @param category the category in question. This is the fully qualified class
212    *          name of the category.
213    * @return <code>true</code> if the category is accepted to be displayed,
214    *         <code>false</code> if it should not be displayed. If the
215    *         <code>category</code> is not know to the system (that is the class
216    *         cannot be found), the category is also accepted and
217    *         <code>true</code> is returned.
218    */
219   public boolean acceptCategory(final String category)
220   {
221     try
222     {
223       final Class<?> type = Class.forName(category);
224       if (excludeCategories == ALL) // NOPMD
225       {
226         return acceptIncludes(type);
227       }
228       else
229       {
230         for (Class<?> excluded : excludeCategories)
231         {
232           if (excluded.isAssignableFrom(type))
233           {
234             return acceptIncludes(type);
235           }
236         }
237         return true;
238       }
239     }
240     catch (final ClassNotFoundException e)
241     {
242       return true;
243     }
244   }
245 
246   private boolean acceptIncludes(final Class<?> type)
247   {
248     if (includeCategories == ALL) // NOPMD
249     {
250       return true;
251     }
252 
253     for (Class<?> included : includeCategories)
254     {
255       if (included.isAssignableFrom(type))
256       {
257         return true;
258       }
259     }
260     return false;
261   }
262 
263   /**
264    * Filters the list of categories to retain only those that are accepted by
265    * {@link #acceptCategory(String)}.
266    *
267    * @param categories the category list to filter.
268    * @return a copy of the given <code>categories</code> containing only those
269    *         categories that are accepted.
270    */
271   public List<String> filterCategories(final List<String> categories)
272   {
273     if (categories == null)
274     {
275       return null;
276     }
277 
278     if (excludeCategories != ALL && excludeCategories.isEmpty()) // NOPMD
279     {
280       return new ArrayList<String>(categories);
281     }
282 
283     final List<String> copy = new ArrayList<String>(categories.size());
284     for (final String category : categories)
285     {
286       if (acceptCategory(category))
287       {
288         copy.add(category);
289       }
290     }
291     return copy;
292   }
293 
294   // --- object basics --------------------------------------------------------
295 
296 }