View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.rat.configuration;
20  
21  import java.lang.reflect.InvocationTargetException;
22  import java.util.HashMap;
23  import java.util.Map;
24  import java.util.Objects;
25  
26  import org.apache.commons.lang3.StringUtils;
27  import org.apache.rat.ConfigurationException;
28  import org.apache.rat.Defaults;
29  import org.apache.rat.configuration.builders.AbstractBuilder;
30  
31  /**
32   * A class to track the Matcher Builders as they are defined.  Matchers may be defined in multiple configuration files
33   * this method tracks them so that they can be referenced across the configuration files.
34   */
35  public class MatcherBuilderTracker {
36  
37      private static MatcherBuilderTracker INSTANCE;
38  
39      private final Map<String, Class<? extends AbstractBuilder>> matcherBuilders;
40  
41      private static synchronized MatcherBuilderTracker instance() {
42          if (INSTANCE == null) {
43              INSTANCE = new MatcherBuilderTracker();
44              Defaults.init();
45          }
46          return INSTANCE;
47      }
48  
49      /**
50       * Adds a builder to the tracker.
51       * If the {@code name} is null then the builder class name simple is used with the "Builder" suffix removed.
52       * @param className the Class name for the builder.
53       * @param name the short name for the builder. 
54       */
55      public static void addBuilder(String className, String name) {
56          instance().addBuilderImpl(className, name);
57      }
58  
59      /**
60       * Get the matching builder for the name.
61       * @param name The name of the builder.
62       * @return the builder for that name.
63       */
64      public static AbstractBuilder getMatcherBuilder(String name) {
65          Class<? extends AbstractBuilder> clazz = instance().matcherBuilders.get(name);
66          if (clazz == null) {
67              StringBuilder sb = new StringBuilder("\nValid builders\n");
68              instance().matcherBuilders.keySet().forEach(x -> sb.append(x).append("\n"));
69              sb.append("ERROR MSG\n");
70              throw new ConfigurationException(sb.append("No matcher builder named ").append(name).toString());
71          }
72          try {
73              return clazz.getConstructor().newInstance();
74          } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException
75                  | IllegalArgumentException | InvocationTargetException e) {
76              throw new ConfigurationException(
77                      String.format("Can not instantiate matcher builder named %s (%s)", name, clazz.getName()), e);
78          }
79      }
80  
81      private MatcherBuilderTracker() {
82          matcherBuilders = new HashMap<>();
83      }
84  
85      private void addBuilderImpl(String className, String name) {
86          Objects.requireNonNull(className, "className may not be null");
87          Class<?> clazz;
88          try {
89              clazz = getClass().getClassLoader().loadClass(className);
90          } catch (ClassNotFoundException e) {
91              throw new ConfigurationException(e);
92          }
93          if (AbstractBuilder.class.isAssignableFrom(clazz)) {
94              @SuppressWarnings("unchecked")
95              Class<? extends AbstractBuilder> candidate = (Class<? extends AbstractBuilder>) clazz;
96              // String name = attributes.get(AttributeName.name);
97              if (StringUtils.isBlank(name)) {
98                  name = candidate.getSimpleName();
99                  if (!name.endsWith("Builder")) {
100                     throw new ConfigurationException(
101                             "name is required, or " + candidate.getName() + " must end with 'Builder'");
102                 }
103                 name = name.substring(0, name.lastIndexOf("Builder"));
104                 if (StringUtils.isBlank(name)) {
105                     throw new ConfigurationException("Last segment of " + candidate.getName()
106                             + " may not be 'Builder', but must end in 'Builder'");
107                 }
108                 name = name.replaceFirst(".", StringUtils.lowerCase(name.substring(0, 1)));
109             }
110             matcherBuilders.put(name, candidate);
111         } else {
112             throw new ConfigurationException("Class " + clazz.getName() + " does not extend " + AbstractBuilder.class);
113         }
114     }
115 }