001 // BSD License (http://www.galagosearch.org/license)
002 package org.galagosearch.tupleflow.typebuilder;
003
004 import java.util.ArrayList;
005 import java.util.Collections;
006 import java.util.HashMap;
007 import java.util.HashSet;
008 import org.antlr.stringtemplate.CommonGroupLoader;
009 import org.antlr.stringtemplate.StringTemplate;
010 import org.antlr.stringtemplate.StringTemplateGroup;
011 import org.antlr.stringtemplate.language.AngleBracketTemplateLexer;
012 import org.galagosearch.tupleflow.typebuilder.FieldSpecification.DataType;
013
014 /**
015 *
016 * @author trevor
017 */
018 public class TemplateTypeBuilder {
019 StringTemplateGroup template;
020 String typeName;
021 String typePackage;
022 ArrayList<Field> typeFields;
023 ArrayList<OrderSpec> typeOrders;
024
025 /**
026 * For an array master, returns
027 * an array containing the last master.length-index elements.
028 */
029 public static String[] subarray(String[] master, int index) {
030 if (master.length <= index) {
031 return new String[0];
032 } else {
033 String[] sub = new String[master.length - index];
034 System.arraycopy(master, index, sub, 0, sub.length);
035 return sub;
036 }
037 }
038
039 /**
040 * Returns a string containing all the elements of args, space delimited.
041 */
042 public static String join(String[] args, String delimiter) {
043 String output = "";
044 StringBuilder builder = new StringBuilder();
045
046 for (String arg : args) {
047 if (builder.length() > 0) {
048 builder.append(delimiter);
049 }
050 builder.append(arg);
051 }
052
053 return builder.toString();
054 }
055
056 public static String join(String[] args) {
057 return join(args, " ");
058 }
059
060 public static String caps(String input) {
061 if (input.length() == 0) {
062 return input;
063 }
064 char first = Character.toUpperCase(input.charAt(0));
065 return "" + first + input.substring(1);
066 }
067
068 public static String plural(String input) {
069 return input + "s";
070 }
071
072 public static class Field {
073 public Field(DataType type, String name) {
074 this.dataType = type;
075 this.type = type.getType();
076 this.name = name;
077 this.capsName = caps(name);
078 this.pluralName = plural(name);
079
080 this.inputType = caps(type.getInternalType());
081 this.baseType = type.getBaseType();
082 this.classTypeName = type.getClassName();
083 this.isArray = dataType.isArray();
084 }
085 public DataType dataType;
086 public String type;
087 public String name;
088 public String baseType;
089 public boolean isInteger;
090 public boolean isString;
091 public boolean isArray;
092 public String classTypeName;
093 public String capsName;
094 public String inputType;
095 public String pluralName;
096 }
097
098 public static class OrderedField extends Field {
099 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 }