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