View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.audit.generator;
18  
19  import java.io.DataOutputStream;
20  import java.io.File;
21  import java.io.FileOutputStream;
22  import java.io.IOException;
23  import java.io.OutputStream;
24  import java.util.ArrayList;
25  import java.util.Collections;
26  import java.util.HashSet;
27  import java.util.Iterator;
28  import java.util.List;
29  import java.util.Set;
30  import java.util.TreeSet;
31  
32  import org.apache.logging.log4j.LogManager;
33  import org.apache.logging.log4j.Logger;
34  import org.apache.logging.log4j.audit.util.NamingUtils;
35  
36  import static org.apache.logging.log4j.audit.generator.Constants.*;
37  
38  /**
39   * Generates the Classes and Interfaces for Audit Logging based on data in the Catalog.
40   */
41  public final class ClassGenerator {
42      private static final Logger LOGGER = LogManager.getLogger(ClassGenerator.class);
43  
44      protected List<AccessorDefinition> beanMethods = new ArrayList<AccessorDefinition>();
45      private boolean isClass = true;
46      private String className;
47      private String parentClassName;
48      private String packageName;
49      private String baseFolder;
50      private String javadocComment;
51      private boolean verbose;
52      private List<String> implementsDeclarations = new ArrayList<>();
53  
54      private Set<String> importsDeclarations = new HashSet<String>();
55  
56      private List<VariableDefinition> localVariables = new ArrayList<>();
57  
58      private List<ConstructorDefinition> constructors = new ArrayList<>();
59  
60      private List<MethodDefinition> methods = new ArrayList<>();
61  
62      private boolean runPrewrite = false;
63  
64      private boolean isAbstract = false;
65  
66      private String visability = PUBLIC;
67  
68      private String annotations = null;
69  
70      private String code = null;
71  
72      private String typeStatement = null;
73  
74      public ClassGenerator(String className, String baseFolder) {
75          this.className = className;
76          this.baseFolder = baseFolder;
77      }
78  
79      public String getTypeStatement() {
80          return typeStatement;
81      }
82  
83      public void setTypeStatement(String typeStatement) {
84          this.typeStatement = typeStatement;
85      }
86  
87      /**
88       * Code is not resolved and is just injected straight into the main code
89       * block of the respective class
90       *
91       * @return
92       */
93  
94      public String getCode() {
95          return code;
96      }
97  
98      public void setCode(String code) {
99          this.code = code;
100     }
101 
102     public void addBeanMethods(AccessorDefinition beanDefinition) {
103         beanMethods.add(beanDefinition);
104     }
105 
106     public void addConstructor(ConstructorDefinition constructorDefinition) {
107         constructors.add(constructorDefinition);
108     }
109 
110     public void addLocalVariable(VariableDefinition definition) {
111         localVariables.add(definition);
112     }
113 
114     public void addMethod(MethodDefinition definition) {
115         methods.add(definition);
116     }
117 
118     public void addSingelton(String name, List<String> parameters) {
119         if (Character.isUpperCase(name.charAt(0))) {
120             name = name.substring(0, 1).toLowerCase() + name.substring(1);
121         }
122 
123         VariableDefinition definition = new VariableDefinition("private",
124                 getClassName(), name, null);
125         definition.setMakeStatic(true);
126         addLocalVariable(definition);
127         addMethod(MethodDefinition.getStandardSingleton(getClassName(), name, parameters));
128     }
129 
130     public String getAnnotations() {
131         return annotations;
132     }
133 
134     public void setAnnotations(String annotations) {
135         this.annotations = annotations;
136     }
137 
138     public List<AccessorDefinition> getBeanMethods() {
139         return beanMethods;
140     }
141 
142     public String getClassName() {
143         return className;
144     }
145 
146     public String getParentClassName() {
147         return parentClassName;
148     }
149 
150     public void setParentClassName(String parentClassName) {
151         this.parentClassName = parentClassName;
152     }
153 
154     public List<String> getImplements() {
155         return implementsDeclarations;
156     }
157 
158     public Set<String> getImports() {
159         return importsDeclarations;
160     }
161 
162     public List<MethodDefinition> getMethodDefinitions() {
163         return methods;
164     }
165 
166     public String getPackageName() {
167         return packageName;
168     }
169 
170     public void setPackageName(String packageName) {
171         this.packageName = packageName;
172     }
173 
174     public List<VariableDefinition> getVariableDefinitions() {
175         return localVariables;
176     }
177 
178     public String getVisability() {
179         return visability;
180     }
181 
182     public void setVisability(String visability) {
183         this.visability = visability;
184     }
185 
186     public boolean isAbstract() {
187         return isAbstract;
188     }
189 
190     public void setAbstract(boolean isAbstract) {
191         this.isAbstract = isAbstract;
192     }
193 
194     public boolean isClass() {
195         return isClass;
196     }
197 
198     public void setClass(boolean isClass) {
199         this.isClass = isClass;
200     }
201 
202     /**
203      * Override this method it gets called once before toString do if toString
204      * gets called 5 time this will only be called on the first
205      */
206     public void preWrite() {
207 
208     }
209 
210     public void generate() throws Exception {
211         StringBuilder sb = new StringBuilder(baseFolder);
212         if (getPackageName() != null) {
213             sb.append("/").append(getPackageName().replaceAll("\\.", "/"));
214         }
215         sb.append("/").append(NamingUtils.upperFirst(getClassName()))
216                 .append(".java");
217         String fullPath = sb.toString();
218         if (verbose) {
219             LOGGER.info(fullPath);
220         }
221         File file = new File(fullPath);
222         DataOutputStream out = new DataOutputStream(openOutputStream(file));
223         out.writeBytes(getClassContents());
224         out.close();
225 
226     }
227 
228     public String getClassContents() throws Exception {
229         if (getClassName() == null) {
230             throw new Exception("Class name has to be set");
231         }
232 
233         if (!runPrewrite) {
234             preWrite();
235             runPrewrite = true;
236         }
237 
238         StringBuilder sb = new StringBuilder();
239         sb.append("package ").append(getPackageName()).append(";\n\n");
240         if (getImports() != null) {
241             List<String> list = new ArrayList<String>(getImports());
242             Collections.sort(list);
243             for (String element : list) {
244                 sb.append("import ").append(element).append(";\n");
245             }
246             sb.append("\n");
247         }
248 
249         sb.append("/**\n");
250         if (getJavadocComment() != null) {
251             sb.append(" * ").append(getJavadocComment());
252         }
253         sb.append("\n * @author generated");
254         sb.append("\n */\n");
255 
256         if (annotations != null) {
257             sb.append(annotations);
258             sb.append("\n");
259         }
260 
261         sb.append(getVisability());
262         if (isClass()) {
263             sb.append(" class ");
264         } else {
265             sb.append(" interface ");
266         }
267         sb.append(getClassName());
268         if (typeStatement != null) {
269             sb.append(" <").append(typeStatement).append("> ");
270         }
271 
272         if (getParentClassName() != null && getParentClassName().length() > 0) {
273             sb.append(" extends ").append(getParentClassName());
274         }
275         if (getImplements() != null && getImplements().size() > 0) {
276             sb.append(" implements ");
277             boolean first = true;
278             for (String element : getImplements()) {
279                 if (!first) {
280                     sb.append(", ");
281                 }
282                 sb.append(element);
283                 first = false;
284             }
285         }
286         sb.append(" {\n\n");
287         if (localVariables != null) {
288             Collections.sort(localVariables);
289             for (VariableDefinition element : localVariables) {
290                 sb.append(element).append("\n");
291             }
292         }
293 
294         if (constructors != null) {
295             Collections.sort(constructors);
296             for (ConstructorDefinition element : constructors) {
297                 sb.append(element).append("\n\n");
298             }
299         }
300 
301         if (beanMethods.size() > 0 && isClass()) {
302             MethodDefinition definition = new MethodDefinition("String",
303                     "toString");
304             StringBuilder buffer = new StringBuilder();
305             buffer.append("\tStringBuilder sb = new StringBuilder();");
306             buffer.append("\n\tsb.append(super.toString());");
307             for (AccessorDefinition element : beanMethods) {
308                 buffer.append("\n\tsb.append(\", ");
309                 buffer.append(element.getName())
310                         .append("=\").append(")
311                         .append(NamingUtils.getAccessorName(element.getName(),
312                                 element.getType())).append("());");
313             }
314             buffer.append("\n\treturn sb.toString();");
315             definition.setContent(buffer.toString());
316             methods.add(definition);
317         }
318 
319         if (methods != null) {
320             Collections.sort(methods);
321             for (MethodDefinition element : methods) {
322                 sb.append(element).append("\n\n");
323             }
324         }
325 
326         if (code != null) {
327             sb.append(code).append("\n");
328         }
329 
330         sb.append("}");
331         return sb.toString();
332     }
333 
334     public String getJavadocComment() {
335         return javadocComment;
336     }
337 
338     public void setJavadocComment(String javadocComment) {
339         this.javadocComment = javadocComment;
340     }
341 
342     private OutputStream openOutputStream(File file) throws IOException {
343         if (file.exists()) {
344             if (file.isDirectory()) {
345                 throw new IOException("File '" + file + "' exists but is a directory");
346             }
347             if (!file.canWrite()) {
348                 throw new IOException("File '" + file + "' cannot be written to");
349             }
350         } else {
351             final File parent = file.getParentFile();
352             if (parent != null) {
353                 if (!parent.mkdirs() && !parent.isDirectory()) {
354                     throw new IOException("Directory '" + parent + "' could not be created");
355                 }
356             }
357         }
358         return new FileOutputStream(file, false);
359     }
360 
361     public void setVerbose(boolean verbose) {
362         this.verbose = verbose;
363     }
364 }