View Javadoc

1   // BSD License (http://www.galagosearch.org/license)
2   package org.galagosearch.tupleflow.typebuilder;
3   
4   import java.util.ArrayList;
5   import java.util.Collections;
6   import java.util.HashMap;
7   import java.util.HashSet;
8   import org.antlr.stringtemplate.CommonGroupLoader;
9   import org.antlr.stringtemplate.StringTemplate;
10  import org.antlr.stringtemplate.StringTemplateGroup;
11  import org.antlr.stringtemplate.language.AngleBracketTemplateLexer;
12  import org.galagosearch.tupleflow.typebuilder.FieldSpecification.DataType;
13  
14  /***
15   *
16   * @author trevor
17   */
18  public class TemplateTypeBuilder {
19      StringTemplateGroup template;
20      String typeName;
21      String typePackage;
22      ArrayList<Field> typeFields;
23      ArrayList<OrderSpec> typeOrders;
24  
25      /***
26       * For an array master, returns
27       * an array containing the last master.length-index elements.
28       */
29      public static String[] subarray(String[] master, int index) {
30          if (master.length <= index) {
31              return new String[0];
32          } else {
33              String[] sub = new String[master.length - index];
34              System.arraycopy(master, index, sub, 0, sub.length);
35              return sub;
36          }
37      }
38  
39      /***
40       * Returns a string containing all the elements of args, space delimited.
41       */
42      public static String join(String[] args, String delimiter) {
43          String output = "";
44          StringBuilder builder = new StringBuilder();
45  
46          for (String arg : args) {
47              if (builder.length() > 0) {
48                  builder.append(delimiter);
49              }
50              builder.append(arg);
51          }
52  
53          return builder.toString();
54      }
55  
56      public static String join(String[] args) {
57          return join(args, " ");
58      }
59  
60      public static String caps(String input) {
61          if (input.length() == 0) {
62              return input;
63          }
64          char first = Character.toUpperCase(input.charAt(0));
65          return "" + first + input.substring(1);
66      }
67  
68      public static String plural(String input) {
69          return input + "s";
70      }
71  
72      public static class Field {
73          public Field(DataType type, String name) {
74              this.dataType = type;
75              this.type = type.getType();
76              this.name = name;
77              this.capsName = caps(name);
78              this.pluralName = plural(name);
79              
80              this.inputType = caps(type.getInternalType());
81              this.baseType = type.getBaseType();
82              this.classTypeName = type.getClassName();
83              this.isArray = dataType.isArray();
84          }
85          public DataType dataType;
86          public String type;
87          public String name;
88          public String baseType;
89          public boolean isInteger;
90          public boolean isString;
91          public boolean isArray;
92          public String classTypeName;
93          public String capsName;
94          public String inputType;
95          public String pluralName;
96      }
97  
98      public static class OrderedField extends Field {
99          public OrderedField(Field field, boolean ascending, boolean delta, boolean runLengthEncoded) {
100             this(field.dataType, field.name, ascending, delta, runLengthEncoded);
101         }
102 
103         public OrderedField(DataType type, String name, boolean ascending, boolean delta, boolean runLengthEncoded) {
104             super(type, name);
105             this.ascending = ascending;
106             this.direction = ascending ? "+" : "-";
107             this.directionName = ascending ? "Ascending" : "Descending";
108             this.runLengthEncoded = runLengthEncoded;
109             this.delta = delta;
110         }
111         public boolean ascending;
112         public String direction;
113         public String directionName;
114         public boolean delta;
115         public boolean runLengthEncoded;
116         public ArrayList<OrderedField> remaining = new ArrayList();
117     }
118 
119     public static class OrderSpec {
120         public OrderSpec(OrderSpecification spec, ArrayList<Field> allFields) {
121             this.allFields = allFields;
122 
123             HashMap<String, Field> fieldMap = new HashMap();
124             HashSet<String> orderedNames = new HashSet();
125 
126             for (Field f : allFields) {
127                 fieldMap.put(f.name, f);
128             }
129 
130             ArrayList<OrderedFieldSpecification> inputFields = spec.getOrderedFields();
131             
132             for (int i = 0; i < inputFields.size(); i++) {
133                 Field field = fieldMap.get(inputFields.get(i).getName());
134                 boolean isLastField = ((inputFields.size() - i) == 1);
135                 // BUGBUG: this is where delta encoding should go
136                 boolean useDelta = false; //isLastField && (field.isInteger || field.isString); 
137                 boolean useRLE = !useDelta;
138                 boolean isAscending = (inputFields.get(i).getDirection() == Direction.ASCENDING);
139                 String fieldName = inputFields.get(i).getName();
140 
141                 if (fieldMap.get(fieldName) == null) {
142                     throw new RuntimeException("'" + fieldName + "' is specified in an order statement, " +
143                                                "but it isn't a type field name.");
144                 }
145                 
146                 OrderedField ordered = new OrderedField(fieldMap.get(fieldName),
147                                                         isAscending, useDelta, useRLE);
148                 orderedFields.add(ordered);
149                 orderedNames.add(fieldName);
150             }
151 
152             for (int i = 0; i < inputFields.size(); i++) {
153                 orderedFields.get(i).remaining.addAll(
154                         orderedFields.subList(i + 1, orderedFields.size()));
155             }
156 
157             backwardOrderedFields.addAll(orderedFields);
158             Collections.reverse(backwardOrderedFields);
159 
160             for (int i = 0; i < orderedFields.size(); i++) {
161                 if (orderedFields.get(i).runLengthEncoded) {
162                     rleFields.add(orderedFields.get(i));
163                 }
164                 if (orderedFields.get(i).delta) {
165                     deltaFields.add(orderedFields.get(i));
166                 }
167             }
168 
169             for (int i = 0; i < orderedFields.size(); i++) {
170                 OrderedField current = orderedFields.get(i);
171                 OrderedField next = ((i + 1) < orderedFields.size()) ? orderedFields.get(i + 1) : null;
172                 OrderedField previous = (i != 0) ? orderedFields.get(i - 1) : null;
173 
174                 fieldPairs.add(new FieldPair(current, next, previous));
175             }
176 
177             for (int i = 0; i < allFields.size(); i++) {
178                 if (orderedNames.contains(allFields.get(i).name)) {
179                     continue;
180                 }
181                 unorderedFields.add(allFields.get(i));
182             }
183         }
184 
185         public String getClassName() {
186             StringBuilder builder = new StringBuilder();
187 
188             if (orderedFields.size() > 0) {
189                 for (OrderedField orderedField : orderedFields) {
190                     if (!orderedField.ascending) {
191                         builder.append("Desc");
192                     }
193                     builder.append(caps(orderedField.name));
194                 }
195 
196                 builder.append("Order");
197             } else {
198                 builder.append("Unordered");
199             }
200 
201             return builder.toString();
202         }
203 
204         public static class FieldPair {
205             public FieldPair(OrderedField c, OrderedField n, OrderedField p) {
206                 current = c;
207                 next = n;
208                 previous = p;
209             }
210             public OrderedField current;
211             public OrderedField next;
212             public OrderedField previous;
213         }
214         public ArrayList<OrderedField> orderedFields = new ArrayList();
215         public ArrayList<OrderedField> backwardOrderedFields = new ArrayList();
216         public ArrayList<FieldPair> fieldPairs = new ArrayList();
217         public ArrayList<OrderedField> rleFields = new ArrayList();
218         public ArrayList<Field> unorderedFields = new ArrayList();
219         public ArrayList<OrderedField> deltaFields = new ArrayList();
220         public ArrayList<Field> allFields;
221     }
222 
223     /***
224      * Returns the text of a Type<T> object for the T class.
225      */
226     @Override
227     public String toString() {
228         StringTemplate t = template.getInstanceOf("type");
229         HashMap<String, Object> map = new HashMap<String, Object>();
230         map.put("typeName", this.typeName);
231         map.put("package", this.typePackage);
232         map.put("orders", this.typeOrders);
233         map.put("fields", this.typeFields);
234 
235         boolean containsArray = false;
236         for (Field f : typeFields) {
237             containsArray = containsArray || f.isArray;
238         }
239         map.put("containsArray", containsArray);
240         t.setAttributes(map);
241         return t.toString();
242     }
243 
244     public TemplateTypeBuilder(TypeSpecification spec) {
245         CommonGroupLoader loader = new CommonGroupLoader("org/galagosearch/tupleflow/templates", null);
246         StringTemplateGroup.registerGroupLoader(loader);
247         StringTemplateGroup.registerDefaultLexer(AngleBracketTemplateLexer.class);
248         this.template = StringTemplateGroup.loadGroup("GalagoType");
249 
250         this.typeName = spec.getTypeName();
251         this.typePackage = spec.getPackageName();
252         this.typeFields = new ArrayList<Field>();
253         this.typeOrders = new ArrayList<OrderSpec>();
254 
255         for (FieldSpecification fieldSpec : spec.getFields()) {
256             Field field = new Field(fieldSpec.getType(), fieldSpec.getName());
257             typeFields.add(field);
258         }
259 
260         for (OrderSpecification orderSpec : spec.getOrders()) {
261             OrderSpec order = new OrderSpec(orderSpec, typeFields);
262             typeOrders.add(order);
263         }
264     }
265     
266     public static void main(String[] args) throws java.lang.Exception {
267         for (String fileName : args) {
268             TypeSpecification spec = ParserDriver.getTypeSpecification(fileName);
269             TemplateTypeBuilder builder = new TemplateTypeBuilder(spec);
270             
271             java.io.FileWriter writer = new java.io.FileWriter(spec.getTypeName() + ".java");
272             String comment = "// This file was automatically generated with the command: \n" +
273                          "//     java org.galagosearch.tupleflow.typebuilder.TemplateTypeBuilder ...\n";
274 
275             writer.write(comment);
276             writer.write(builder.toString());
277             writer.close();
278         }
279     }
280 }