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, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.giraph.generate;
19  
20  import org.junit.Test;
21  
22  import java.io.File;
23  import java.io.FileNotFoundException;
24  import java.io.FileOutputStream;
25  import java.io.IOException;
26  import java.io.OutputStreamWriter;
27  import java.io.Writer;
28  import java.util.EnumSet;
29  import java.util.HashMap;
30  import java.util.Map;
31  
32  import freemarker.core.ParseException;
33  import freemarker.template.Configuration;
34  import freemarker.template.MalformedTemplateNameException;
35  import freemarker.template.Template;
36  import freemarker.template.TemplateException;
37  import freemarker.template.TemplateExceptionHandler;
38  import freemarker.template.TemplateNotFoundException;
39  
40  /**
41   * <p>Code generation utility that generates set of classes from a template files.
42   * Templates are found in giraph-core/template/ folder.
43   * If you want to add new generation, look at the main function, and add a call
44   * to appropriate generate* function.</p>
45   *
46   * <p>Templates are using freemarker template format, which given
47   * template file and map of String-&gt;Object replacements generates
48   * new file.</p>
49   * Main rules:
50   * <ul>
51   * <li><code>${something}</code> gets replaced with value of <code>map.get("something")</code></li>
52   * <li><code>${obj.method}</code> gets replaced with value of <code>map.get("obj").getMethod()</code></li>
53   * </ul>
54   * More description about template format can be found at:
55   * <a href="http://freemarker.org/docs/dgui_quickstart_template.html">tutorial</a>
56   */
57  public class GeneratePrimitiveClasses {
58    public static enum PrimitiveType {
59      BOOLEAN("Boolean"),
60      BYTE("Byte"),
61      SHORT("Short"),
62      INT("Int"),
63      LONG("Long"),
64      FLOAT("Float"),
65      DOUBLE("Double");
66  
67      private final String name;
68      private final String nameLower;
69      private final String boxed;
70      private final boolean numeric;
71      private final boolean id;
72      private final boolean floating;
73      private final boolean hasWritable;
74  
75      private PrimitiveType(String name) {
76        this.name = name;
77        this.nameLower = name.toLowerCase();
78        this.boxed = "Int".equals(name) ? "Integer" : name;
79        this.numeric = !"Boolean".equals(name);
80        this.id = "Int".equals(name) || "Long".equals(name);
81        this.floating = "Float".equals(name) || "Double".equals(name);
82        // For some reason there is no ShortWritable in current Hadoop version
83        this.hasWritable = !"Short".equals(name);
84      }
85  
86      public String getName() {
87        return name;
88      }
89  
90      public String getCamel() {
91        return name;
92      }
93  
94      public String getLower() {
95        return nameLower;
96      }
97  
98      public String getBoxed() {
99        return boxed;
100     }
101 
102     public boolean isNumeric() {
103       return numeric;
104     }
105 
106     public boolean isId() {
107       return id;
108     }
109 
110     public boolean isFloating() {
111       return floating;
112     }
113 
114     public boolean hasWritable() {
115       return hasWritable;
116     }
117   }
118 
119   public static void main(String[] args) throws Exception {
120     /* Create and adjust the configuration singleton */
121     Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);
122     cfg.setDirectoryForTemplateLoading(new File("templates"));
123     cfg.setDefaultEncoding("UTF-8");
124     cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
125 
126     String[] primitiveFunctions = { "%sConsumer", "%sPredicate", "Obj2%sFunction" };
127 
128     for (String function: primitiveFunctions) {
129       generateForAll(
130           cfg,
131           EnumSet.allOf(PrimitiveType.class),
132           String.format(function, "Type") + ".java",
133           "src/main/java/org/apache/giraph/function/primitive/" + function + ".java");
134     }
135 
136     EnumSet<PrimitiveType> writableSet = EnumSet.noneOf(PrimitiveType.class);
137     EnumSet<PrimitiveType> ids = EnumSet.noneOf(PrimitiveType.class);
138     EnumSet<PrimitiveType> numerics = EnumSet.noneOf(PrimitiveType.class);
139     for (PrimitiveType type : EnumSet.allOf(PrimitiveType.class)) {
140       if (type.hasWritable()) {
141         writableSet.add(type);
142         if (type.isId()) {
143           ids.add(type);
144         }
145         if (type.isNumeric()) {
146           numerics.add(type);
147         }
148       }
149     }
150 
151     generateForAll(
152         cfg,
153         writableSet,
154         "TypeTypeOps.java",
155         "src/main/java/org/apache/giraph/types/ops/%sTypeOps.java");
156 
157     generateForAll(
158         cfg,
159         writableSet,
160         "WTypeCollection.java",
161         "src/main/java/org/apache/giraph/types/ops/collections/W%sCollection.java");
162 
163     generateForAll(
164         cfg,
165         writableSet,
166         "WTypeArrayList.java",
167         "src/main/java/org/apache/giraph/types/ops/collections/array/W%sArrayList.java");
168 
169     generateForAll(
170         cfg,
171         writableSet,
172         writableSet,
173         "TypeTypeConsumer.java",
174         "src/main/java/org/apache/giraph/function/primitive/pairs/%s%sConsumer.java");
175 
176     generateForAll(
177         cfg,
178         writableSet,
179         writableSet,
180         "TypeTypePredicate.java",
181         "src/main/java/org/apache/giraph/function/primitive/pairs/%s%sPredicate.java");
182 
183     generateForAll(
184         cfg,
185         ids,
186         numerics,
187         "Type2TypeMapEntryIterable.java",
188         "src/main/java/org/apache/giraph/types/heaps/%s2%sMapEntryIterable.java");
189 
190     generateForAll(
191         cfg,
192         ids,
193         numerics,
194         "FixedCapacityType2TypeMinHeap.java",
195         "src/main/java/org/apache/giraph/types/heaps/FixedCapacity%s%sMinHeap.java");
196 
197     generateForAll(
198         cfg,
199         ids,
200         numerics,
201         "TestFixedCapacityType2TypeMinHeap.java",
202         "src/test/java/org/apache/giraph/types/heaps/TestFixedCapacity%s%sMinHeap.java");
203 
204     System.out.println("Successfully generated classes");
205   }
206 
207   /**
208    * Generate a set of files from a template, one for each type in the passed set,
209    * where added entry for "type" to that object is added, on top of default entries.
210    */
211   private static void generateForAll(Configuration cfg, EnumSet<PrimitiveType> types,
212       String template, String outputPattern) throws TemplateNotFoundException, MalformedTemplateNameException, ParseException, FileNotFoundException, IOException, TemplateException {
213     for (PrimitiveType type : types) {
214       Map<String, Object> props = defaultMap();
215       props.put("type", type);
216       generateAndWrite(cfg, props,
217           template,
218           String.format(outputPattern, type.getCamel()));
219     }
220   }
221 
222   /**
223    * Generate a set of files from a template, one for each combination of
224    * types in the passed sets, where added entry for "type1" and "type2" to
225    * that object is added, on top of default entries.
226    */
227   private static void generateForAll(Configuration cfg,
228       EnumSet<PrimitiveType> types1, EnumSet<PrimitiveType> types2,
229       String template, String outputPattern) throws TemplateNotFoundException, MalformedTemplateNameException, ParseException, FileNotFoundException, IOException, TemplateException {
230     for (PrimitiveType type1 : types1) {
231       for (PrimitiveType type2 : types2) {
232         Map<String, Object> props = defaultMap();
233         props.put("type1", type1);
234         props.put("type2", type2);
235         generateAndWrite(cfg, props,
236             template,
237             String.format(outputPattern, type1.getCamel(), type2.getCamel()));
238       }
239     }
240   }
241 
242   /** Generate a single file from a template, replacing mappings from given properties */
243   private static void generateAndWrite(Configuration cfg, Map<String, Object> props,
244       String template, String outputFile)
245           throws TemplateNotFoundException, MalformedTemplateNameException, ParseException,
246           IOException, FileNotFoundException, TemplateException {
247     Template temp = cfg.getTemplate(template);
248     Writer out = new OutputStreamWriter(new FileOutputStream(outputFile));
249     temp.process(props, out);
250     out.close();
251   }
252 
253   public static Map<String, Object> defaultMap() {
254     Map<String, Object> props = new HashMap<>();
255     props.put("generated_message", GENERATED_MESSAGE);
256     return props;
257   }
258 
259   private static final String GENERATED_MESSAGE =
260       "// AUTO-GENERATED class via class:\n// " + GeneratePrimitiveClasses.class.getName();
261 }