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  
20  package org.apache.rat.config.parameters;
21  
22  import java.lang.reflect.Field;
23  import java.lang.reflect.Method;
24  import java.util.ArrayList;
25  import java.util.Arrays;
26  import java.util.Collections;
27  import java.util.List;
28  
29  import org.apache.commons.lang3.StringUtils;
30  import org.apache.rat.ConfigurationException;
31  import org.apache.rat.ImplementationException;
32  import org.apache.rat.analysis.IHeaderMatcher;
33  import org.apache.rat.license.ILicense;
34  
35  /**
36   * Builds Description objects for the various Component instances.
37   */
38  public class DescriptionBuilder {
39      /* do not instantiate */
40      private DescriptionBuilder() {}
41  
42      /**
43       * Create the description for the object.
44       * The object must have a ConfigComponent annotation or null will be returned.
45       * @param obj the object to process.
46       * @return the Description of the object.
47       */
48      public static Description build(Object obj) {
49          if (obj instanceof ILicense) {
50              ILicense license = (ILicense)obj;
51              Class<?> clazz = obj.getClass();
52              ConfigComponent configComponent = clazz.getAnnotation(ConfigComponent.class);
53              if (configComponent == null || configComponent.type() != ComponentType.LICENSE) {
54                  throw new ConfigurationException(String.format("Licenses must have License type specified in ConfigComponent annotation.  Annotation missing or incorrect in %s", clazz));
55              }
56              List<Description> children = getConfigComponents(obj.getClass());
57              return new Description(ComponentType.LICENSE, license.getId(), license.getName(), false, null, children, false);
58          }
59          return buildMap(obj.getClass());
60      }
61  
62      private static String fixupMethodName(Method method) {
63          String name = method.getName();
64          if (name.startsWith("get") || name.startsWith("set") || name.startsWith("add")) {
65              if (name.length() > 3) {
66                  String retval = name.substring(3,4).toLowerCase();
67                  if (name.length() > 4) {
68                      retval+=name.substring(4);
69                  }
70                  return retval;
71              }
72          } 
73          throw new ImplementationException(String.format("'%s' is not a recognized method name", name));
74      }
75      /**
76       * Build the list of descriptions for children of the class.
77       * @param clazz
78       * @return the Descriptions. of the child elements.
79       */
80      private static List<Description> getConfigComponents(Class<?> clazz) {
81          if (clazz == null || clazz == String.class || clazz == Object.class) {
82              return Collections.emptyList();
83          }
84          List<Description> result = new ArrayList<>();
85          for (Field field : clazz.getDeclaredFields()) {
86              ConfigComponent configComponent = field.getAnnotation(ConfigComponent.class);
87              if (configComponent != null) {
88                  String name = StringUtils.isBlank(configComponent.name()) ? field.getName() : configComponent.name();
89                  Class<?> childClazz = configComponent.parameterType() == void.class ? field.getType()
90                          : configComponent.parameterType();
91                  boolean isCollection = Iterable.class.isAssignableFrom(field.getType());
92  
93                  Description desc = new Description(configComponent.type(), name, configComponent.desc(), isCollection,
94                          childClazz, getConfigComponents(childClazz), configComponent.required());
95                  result.add(desc);
96              }
97          }
98          for (Method method : clazz.getDeclaredMethods()) {
99              ConfigComponent configComponent = method.getAnnotation(ConfigComponent.class);
100             if (configComponent != null) {
101                 String name = StringUtils.isBlank(configComponent.name()) ? fixupMethodName(method) : configComponent.name();
102                 Class<?> childClazz = configComponent.parameterType() == void.class ? method.getReturnType()
103                         : configComponent.parameterType();
104                 boolean isCollection = Iterable.class.isAssignableFrom(method.getReturnType());
105 
106                 Description desc = new Description(configComponent.type(), name, configComponent.desc(), isCollection,
107                         childClazz, getConfigComponents(childClazz), configComponent.required());
108                 result.add(desc);
109             }
110         }
111         result.addAll(getConfigComponents(clazz.getSuperclass()));
112         Arrays.stream(clazz.getInterfaces()).forEach(c -> result.addAll(getConfigComponents(c)));
113         return result;
114     }
115 
116     private static ConfigComponent findConfigComponent(Class<?> clazz) {
117         if (clazz == null || clazz == String.class || clazz == Object.class) {
118             return null;
119         }
120         ConfigComponent configComponent = clazz.getAnnotation(ConfigComponent.class);
121         return configComponent == null ? findConfigComponent(clazz.getSuperclass()) : configComponent;
122     }
123     /**
124      * Create a description for a class.
125      * @param clazz the class to build the description for.
126      * @return the Description of the class or null if no ConfigComponent annotation was found on the class.
127      */
128     public static Description buildMap(Class<?> clazz) {
129         if (clazz == IHeaderMatcher.class) {
130             throw new ImplementationException("'clazz' parameter must not be IHeaderMatcher.class but may be a child of it"); 
131         }
132         ConfigComponent configComponent = findConfigComponent(clazz);
133         if (configComponent == null) {
134             return null;
135         }
136         List<Description> children = getConfigComponents(clazz);
137 
138         return new Description(configComponent, false, null, children);
139     }
140 }