001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019package org.apache.hadoop.record.compiler;
020
021import java.io.File;
022import java.io.FileWriter;
023import java.io.IOException;
024import java.util.*;
025
026import org.apache.hadoop.classification.InterfaceAudience;
027import org.apache.hadoop.classification.InterfaceStability;
028
029/**
030 * @deprecated Replaced by <a href="https://hadoop.apache.org/avro/">Avro</a>.
031 */
032@Deprecated
033@InterfaceAudience.Public
034@InterfaceStability.Stable
035public class JRecord extends JCompType {
036  
037  class JavaRecord extends JavaCompType {
038    
039    private String fullName;
040    private String name;
041    private String module;
042    private ArrayList<JField<JavaType>> fields =
043      new ArrayList<JField<JavaType>>();
044    
045    JavaRecord(String name, ArrayList<JField<JType>> flist) {
046      super(name, "Record", name, "TypeID.RIOType.STRUCT");
047      this.fullName = name;
048      int idx = name.lastIndexOf('.');
049      this.name = name.substring(idx+1);
050      this.module = name.substring(0, idx);
051      for (Iterator<JField<JType>> iter = flist.iterator(); iter.hasNext();) {
052        JField<JType> f = iter.next();
053        fields.add(new JField<JavaType>(f.getName(), f.getType().getJavaType()));
054      }
055    }
056    
057    String getTypeIDObjectString() {
058      return "new org.apache.hadoop.record.meta.StructTypeID(" + 
059      fullName + ".getTypeInfo())";
060    }
061
062    void genSetRTIFilter(CodeBuffer cb, Map<String, Integer> nestedStructMap) {
063      // ignore, if we'ev already set the type filter for this record
064      if (!nestedStructMap.containsKey(fullName)) {
065        // we set the RTI filter here
066        cb.append(fullName + ".setTypeFilter(rti.getNestedStructTypeInfo(\""+
067            name + "\"));\n");
068        nestedStructMap.put(fullName, null);
069      }
070    }
071
072    // for each typeInfo in the filter, we see if there's a similar one in the record. 
073    // Since we store typeInfos in ArrayLists, thsi search is O(n squared). We do it faster
074    // if we also store a map (of TypeInfo to index), but since setupRtiFields() is called
075    // only once when deserializing, we're sticking with the former, as the code is easier.  
076    void genSetupRtiFields(CodeBuffer cb) {
077      cb.append("private static void setupRtiFields()\n{\n");
078      cb.append("if (null == " + Consts.RTI_FILTER + ") return;\n");
079      cb.append("// we may already have done this\n");
080      cb.append("if (null != " + Consts.RTI_FILTER_FIELDS + ") return;\n");
081      cb.append("int " + Consts.RIO_PREFIX + "i, " + Consts.RIO_PREFIX + "j;\n");
082      cb.append(Consts.RTI_FILTER_FIELDS + " = new int [" + 
083          Consts.RIO_PREFIX + "rtiFilter.getFieldTypeInfos().size()];\n");
084      cb.append("for (" + Consts.RIO_PREFIX + "i=0; " + Consts.RIO_PREFIX + "i<"+
085          Consts.RTI_FILTER_FIELDS + ".length; " + Consts.RIO_PREFIX + "i++) {\n");
086      cb.append(Consts.RTI_FILTER_FIELDS + "[" + Consts.RIO_PREFIX + "i] = 0;\n");
087      cb.append("}\n");
088      cb.append("java.util.Iterator<org.apache.hadoop.record.meta." +
089          "FieldTypeInfo> " + Consts.RIO_PREFIX + "itFilter = " + 
090          Consts.RIO_PREFIX + "rtiFilter.getFieldTypeInfos().iterator();\n");
091      cb.append(Consts.RIO_PREFIX + "i=0;\n");
092      cb.append("while (" + Consts.RIO_PREFIX + "itFilter.hasNext()) {\n");
093      cb.append("org.apache.hadoop.record.meta.FieldTypeInfo " + 
094          Consts.RIO_PREFIX + "tInfoFilter = " + 
095          Consts.RIO_PREFIX + "itFilter.next();\n");
096      cb.append("java.util.Iterator<org.apache.hadoop.record.meta." + 
097          "FieldTypeInfo> " + Consts.RIO_PREFIX + "it = " + Consts.RTI_VAR + 
098          ".getFieldTypeInfos().iterator();\n");
099      cb.append(Consts.RIO_PREFIX + "j=1;\n");
100      cb.append("while (" + Consts.RIO_PREFIX + "it.hasNext()) {\n");
101      cb.append("org.apache.hadoop.record.meta.FieldTypeInfo " + 
102          Consts.RIO_PREFIX + "tInfo = " + Consts.RIO_PREFIX + "it.next();\n");
103      cb.append("if (" + Consts.RIO_PREFIX + "tInfo.equals(" +  
104          Consts.RIO_PREFIX + "tInfoFilter)) {\n");
105      cb.append(Consts.RTI_FILTER_FIELDS + "[" + Consts.RIO_PREFIX + "i] = " +
106          Consts.RIO_PREFIX + "j;\n");
107      cb.append("break;\n");
108      cb.append("}\n");
109      cb.append(Consts.RIO_PREFIX + "j++;\n");
110      cb.append("}\n");
111      /*int ct = 0;
112      for (Iterator<JField<JavaType>> i = fields.iterator(); i.hasNext();) {
113        ct++;
114        JField<JavaType> jf = i.next();
115        JavaType type = jf.getType();
116        String name = jf.getName();
117        if (ct != 1) {
118          cb.append("else ");
119        }
120        type.genRtiFieldCondition(cb, name, ct);
121      }
122      if (ct != 0) {
123        cb.append("else {\n");
124        cb.append("rtiFilterFields[i] = 0;\n");
125        cb.append("}\n");
126      }*/
127      cb.append(Consts.RIO_PREFIX + "i++;\n");
128      cb.append("}\n");
129      cb.append("}\n");
130    }
131
132    void genReadMethod(CodeBuffer cb, String fname, String tag, boolean decl) {
133      if (decl) {
134        cb.append(fullName+" "+fname+";\n");
135      }
136      cb.append(fname+"= new "+fullName+"();\n");
137      cb.append(fname+".deserialize(" + Consts.RECORD_INPUT + ",\""+tag+"\");\n");
138    }
139    
140    void genWriteMethod(CodeBuffer cb, String fname, String tag) {
141      cb.append(fname+".serialize(" + Consts.RECORD_OUTPUT + ",\""+tag+"\");\n");
142    }
143    
144    void genSlurpBytes(CodeBuffer cb, String b, String s, String l) {
145      cb.append("{\n");
146      cb.append("int r = "+fullName+
147                ".Comparator.slurpRaw("+b+","+s+","+l+");\n");
148      cb.append(s+"+=r; "+l+"-=r;\n");
149      cb.append("}\n");
150    }
151    
152    void genCompareBytes(CodeBuffer cb) {
153      cb.append("{\n");
154      cb.append("int r1 = "+fullName+
155                ".Comparator.compareRaw(b1,s1,l1,b2,s2,l2);\n");
156      cb.append("if (r1 <= 0) { return r1; }\n");
157      cb.append("s1+=r1; s2+=r1; l1-=r1; l2-=r1;\n");
158      cb.append("}\n");
159    }
160    
161    void genCode(String destDir, ArrayList<String> options) throws IOException {
162      String pkg = module;
163      String pkgpath = pkg.replaceAll("\\.", "/");
164      File pkgdir = new File(destDir, pkgpath);
165
166      final File jfile = new File(pkgdir, name+".java");
167      if (!pkgdir.exists()) {
168        // create the pkg directory
169        boolean ret = pkgdir.mkdirs();
170        if (!ret) {
171          throw new IOException("Cannnot create directory: "+pkgpath);
172        }
173      } else if (!pkgdir.isDirectory()) {
174        // not a directory
175        throw new IOException(pkgpath+" is not a directory.");
176      }
177
178      CodeBuffer cb = new CodeBuffer();
179      cb.append("// File generated by hadoop record compiler. Do not edit.\n");
180      cb.append("package "+module+";\n\n");
181      cb.append("public class "+name+
182                " extends org.apache.hadoop.record.Record {\n");
183      
184      // type information declarations
185      cb.append("private static final " + 
186          "org.apache.hadoop.record.meta.RecordTypeInfo " + 
187          Consts.RTI_VAR + ";\n");
188      cb.append("private static " + 
189          "org.apache.hadoop.record.meta.RecordTypeInfo " + 
190          Consts.RTI_FILTER + ";\n");
191      cb.append("private static int[] " + Consts.RTI_FILTER_FIELDS + ";\n");
192      
193      // static init for type information
194      cb.append("static {\n");
195      cb.append(Consts.RTI_VAR + " = " +
196          "new org.apache.hadoop.record.meta.RecordTypeInfo(\"" +
197          name + "\");\n");
198      for (Iterator<JField<JavaType>> i = fields.iterator(); i.hasNext();) {
199        JField<JavaType> jf = i.next();
200        String name = jf.getName();
201        JavaType type = jf.getType();
202        type.genStaticTypeInfo(cb, name);
203      }
204      cb.append("}\n\n");
205
206      // field definitions
207      for (Iterator<JField<JavaType>> i = fields.iterator(); i.hasNext();) {
208        JField<JavaType> jf = i.next();
209        String name = jf.getName();
210        JavaType type = jf.getType();
211        type.genDecl(cb, name);
212      }
213
214      // default constructor
215      cb.append("public "+name+"() { }\n");
216      
217      // constructor
218      cb.append("public "+name+"(\n");
219      int fIdx = 0;
220      for (Iterator<JField<JavaType>> i = fields.iterator(); i.hasNext(); fIdx++) {
221        JField<JavaType> jf = i.next();
222        String name = jf.getName();
223        JavaType type = jf.getType();
224        type.genConstructorParam(cb, name);
225        cb.append((!i.hasNext())?"":",\n");
226      }
227      cb.append(") {\n");
228      fIdx = 0;
229      for (Iterator<JField<JavaType>> i = fields.iterator(); i.hasNext(); fIdx++) {
230        JField<JavaType> jf = i.next();
231        String name = jf.getName();
232        JavaType type = jf.getType();
233        type.genConstructorSet(cb, name);
234      }
235      cb.append("}\n");
236
237      // getter/setter for type info
238      cb.append("public static org.apache.hadoop.record.meta.RecordTypeInfo"
239              + " getTypeInfo() {\n");
240      cb.append("return " + Consts.RTI_VAR + ";\n");
241      cb.append("}\n");
242      cb.append("public static void setTypeFilter("
243          + "org.apache.hadoop.record.meta.RecordTypeInfo rti) {\n");
244      cb.append("if (null == rti) return;\n");
245      cb.append(Consts.RTI_FILTER + " = rti;\n");
246      cb.append(Consts.RTI_FILTER_FIELDS + " = null;\n");
247      // set RTIFilter for nested structs.
248      // To prevent setting up the type filter for the same struct more than once, 
249      // we use a hash map to keep track of what we've set. 
250      Map<String, Integer> nestedStructMap = new HashMap<String, Integer>();
251      for (JField<JavaType> jf : fields) {
252        JavaType type = jf.getType();
253        type.genSetRTIFilter(cb, nestedStructMap);
254      }
255      cb.append("}\n");
256
257      // setupRtiFields()
258      genSetupRtiFields(cb);
259
260      // getters/setters for member variables
261      for (Iterator<JField<JavaType>> i = fields.iterator(); i.hasNext();) {
262        JField<JavaType> jf = i.next();
263        String name = jf.getName();
264        JavaType type = jf.getType();
265        type.genGetSet(cb, name);
266      }
267      
268      // serialize()
269      cb.append("public void serialize("+ 
270          "final org.apache.hadoop.record.RecordOutput " + 
271          Consts.RECORD_OUTPUT + ", final String " + Consts.TAG + ")\n"+
272                "throws java.io.IOException {\n");
273      cb.append(Consts.RECORD_OUTPUT + ".startRecord(this," + Consts.TAG + ");\n");
274      for (Iterator<JField<JavaType>> i = fields.iterator(); i.hasNext();) {
275        JField<JavaType> jf = i.next();
276        String name = jf.getName();
277        JavaType type = jf.getType();
278        type.genWriteMethod(cb, name, name);
279      }
280      cb.append(Consts.RECORD_OUTPUT + ".endRecord(this," + Consts.TAG+");\n");
281      cb.append("}\n");
282
283      // deserializeWithoutFilter()
284      cb.append("private void deserializeWithoutFilter("+
285                "final org.apache.hadoop.record.RecordInput " + 
286                Consts.RECORD_INPUT + ", final String " + Consts.TAG + ")\n"+
287                "throws java.io.IOException {\n");
288      cb.append(Consts.RECORD_INPUT + ".startRecord(" + Consts.TAG + ");\n");
289      for (Iterator<JField<JavaType>> i = fields.iterator(); i.hasNext();) {
290        JField<JavaType> jf = i.next();
291        String name = jf.getName();
292        JavaType type = jf.getType();
293        type.genReadMethod(cb, name, name, false);
294      }
295      cb.append(Consts.RECORD_INPUT + ".endRecord(" + Consts.TAG+");\n");
296      cb.append("}\n");
297      
298      // deserialize()
299      cb.append("public void deserialize(final " +
300          "org.apache.hadoop.record.RecordInput " + 
301          Consts.RECORD_INPUT + ", final String " + Consts.TAG + ")\n"+
302          "throws java.io.IOException {\n");
303      cb.append("if (null == " + Consts.RTI_FILTER + ") {\n");
304      cb.append("deserializeWithoutFilter(" + Consts.RECORD_INPUT + ", " + 
305          Consts.TAG + ");\n");
306      cb.append("return;\n");
307      cb.append("}\n");
308      cb.append("// if we're here, we need to read based on version info\n");
309      cb.append(Consts.RECORD_INPUT + ".startRecord(" + Consts.TAG + ");\n");
310      cb.append("setupRtiFields();\n");
311      cb.append("for (int " + Consts.RIO_PREFIX + "i=0; " + Consts.RIO_PREFIX + 
312          "i<" + Consts.RTI_FILTER + ".getFieldTypeInfos().size(); " + 
313          Consts.RIO_PREFIX + "i++) {\n");
314      int ct = 0;
315      for (Iterator<JField<JavaType>> i = fields.iterator(); i.hasNext();) {
316        JField<JavaType> jf = i.next();
317        String name = jf.getName();
318        JavaType type = jf.getType();
319        ct++;
320        if (1 != ct) {
321          cb.append("else ");
322        }
323        cb.append("if (" + ct + " == " + Consts.RTI_FILTER_FIELDS + "[" +
324            Consts.RIO_PREFIX + "i]) {\n");
325        type.genReadMethod(cb, name, name, false);
326        cb.append("}\n");
327      }
328      if (0 != ct) {
329        cb.append("else {\n");
330        cb.append("java.util.ArrayList<"
331                + "org.apache.hadoop.record.meta.FieldTypeInfo> typeInfos = "
332                + "(java.util.ArrayList<"
333                + "org.apache.hadoop.record.meta.FieldTypeInfo>)"
334                + "(" + Consts.RTI_FILTER + ".getFieldTypeInfos());\n");
335        cb.append("org.apache.hadoop.record.meta.Utils.skip(" + 
336            Consts.RECORD_INPUT + ", " + "typeInfos.get(" + Consts.RIO_PREFIX + 
337            "i).getFieldID(), typeInfos.get(" + 
338            Consts.RIO_PREFIX + "i).getTypeID());\n");
339        cb.append("}\n");
340      }
341      cb.append("}\n");
342      cb.append(Consts.RECORD_INPUT + ".endRecord(" + Consts.TAG+");\n");
343      cb.append("}\n");
344
345      // compareTo()
346      cb.append("public int compareTo (final Object " + Consts.RIO_PREFIX + 
347          "peer_) throws ClassCastException {\n");
348      cb.append("if (!(" + Consts.RIO_PREFIX + "peer_ instanceof "+name+")) {\n");
349      cb.append("throw new ClassCastException(\"Comparing different types of records.\");\n");
350      cb.append("}\n");
351      cb.append(name+" " + Consts.RIO_PREFIX + "peer = ("+name+") " + 
352          Consts.RIO_PREFIX + "peer_;\n");
353      cb.append("int " + Consts.RIO_PREFIX + "ret = 0;\n");
354      for (Iterator<JField<JavaType>> i = fields.iterator(); i.hasNext();) {
355        JField<JavaType> jf = i.next();
356        String name = jf.getName();
357        JavaType type = jf.getType();
358        type.genCompareTo(cb, name, Consts.RIO_PREFIX + "peer."+name);
359        cb.append("if (" + Consts.RIO_PREFIX + "ret != 0) return " + 
360            Consts.RIO_PREFIX + "ret;\n");
361      }
362      cb.append("return " + Consts.RIO_PREFIX + "ret;\n");
363      cb.append("}\n");
364      
365      // equals()
366      cb.append("public boolean equals(final Object " + Consts.RIO_PREFIX + 
367          "peer_) {\n");
368      cb.append("if (!(" + Consts.RIO_PREFIX + "peer_ instanceof "+name+")) {\n");
369      cb.append("return false;\n");
370      cb.append("}\n");
371      cb.append("if (" + Consts.RIO_PREFIX + "peer_ == this) {\n");
372      cb.append("return true;\n");
373      cb.append("}\n");
374      cb.append(name+" " + Consts.RIO_PREFIX + "peer = ("+name+") " + 
375          Consts.RIO_PREFIX + "peer_;\n");
376      cb.append("boolean " + Consts.RIO_PREFIX + "ret = false;\n");
377      for (Iterator<JField<JavaType>> i = fields.iterator(); i.hasNext();) {
378        JField<JavaType> jf = i.next();
379        String name = jf.getName();
380        JavaType type = jf.getType();
381        type.genEquals(cb, name, Consts.RIO_PREFIX + "peer."+name);
382        cb.append("if (!" + Consts.RIO_PREFIX + "ret) return " + 
383            Consts.RIO_PREFIX + "ret;\n");
384      }
385      cb.append("return " + Consts.RIO_PREFIX + "ret;\n");
386      cb.append("}\n");
387
388      // clone()
389      cb.append("public Object clone() throws CloneNotSupportedException {\n");
390      cb.append(name+" " + Consts.RIO_PREFIX + "other = new "+name+"();\n");
391      for (Iterator<JField<JavaType>> i = fields.iterator(); i.hasNext();) {
392        JField<JavaType> jf = i.next();
393        String name = jf.getName();
394        JavaType type = jf.getType();
395        type.genClone(cb, name);
396      }
397      cb.append("return " + Consts.RIO_PREFIX + "other;\n");
398      cb.append("}\n");
399      
400      cb.append("public int hashCode() {\n");
401      cb.append("int " + Consts.RIO_PREFIX + "result = 17;\n");
402      cb.append("int " + Consts.RIO_PREFIX + "ret;\n");
403      for (Iterator<JField<JavaType>> i = fields.iterator(); i.hasNext();) {
404        JField<JavaType> jf = i.next();
405        String name = jf.getName();
406        JavaType type = jf.getType();
407        type.genHashCode(cb, name);
408        cb.append(Consts.RIO_PREFIX + "result = 37*" + Consts.RIO_PREFIX + 
409            "result + " + Consts.RIO_PREFIX + "ret;\n");
410      }
411      cb.append("return " + Consts.RIO_PREFIX + "result;\n");
412      cb.append("}\n");
413      
414      cb.append("public static String signature() {\n");
415      cb.append("return \""+getSignature()+"\";\n");
416      cb.append("}\n");
417      
418      cb.append("public static class Comparator extends"+
419                " org.apache.hadoop.record.RecordComparator {\n");
420      cb.append("public Comparator() {\n");
421      cb.append("super("+name+".class);\n");
422      cb.append("}\n");
423      
424      cb.append("static public int slurpRaw(byte[] b, int s, int l) {\n");
425      cb.append("try {\n");
426      cb.append("int os = s;\n");
427      for (Iterator<JField<JavaType>> i = fields.iterator(); i.hasNext();) {
428        JField<JavaType> jf = i.next();
429        String name = jf.getName();
430        JavaType type = jf.getType();
431        type.genSlurpBytes(cb, "b","s","l");
432      }
433      cb.append("return (os - s);\n");
434      cb.append("} catch(java.io.IOException e) {\n");
435      cb.append("throw new RuntimeException(e);\n");
436      cb.append("}\n");
437      cb.append("}\n");
438      
439      cb.append("static public int compareRaw(byte[] b1, int s1, int l1,\n");
440      cb.append("                             byte[] b2, int s2, int l2) {\n");
441      cb.append("try {\n");
442      cb.append("int os1 = s1;\n");
443      for (Iterator<JField<JavaType>> i = fields.iterator(); i.hasNext();) {
444        JField<JavaType> jf = i.next();
445        String name = jf.getName();
446        JavaType type = jf.getType();
447        type.genCompareBytes(cb);
448      }
449      cb.append("return (os1 - s1);\n");
450      cb.append("} catch(java.io.IOException e) {\n");
451      cb.append("throw new RuntimeException(e);\n");
452      cb.append("}\n");
453      cb.append("}\n");
454      cb.append("public int compare(byte[] b1, int s1, int l1,\n");
455      cb.append("                   byte[] b2, int s2, int l2) {\n");
456      cb.append("int ret = compareRaw(b1,s1,l1,b2,s2,l2);\n");
457      cb.append("return (ret == -1)? -1 : ((ret==0)? 1 : 0);");
458      cb.append("}\n");
459      cb.append("}\n\n");
460      cb.append("static {\n");
461      cb.append("org.apache.hadoop.record.RecordComparator.define("
462                +name+".class, new Comparator());\n");
463      cb.append("}\n");
464      cb.append("}\n");
465
466      FileWriter jj = new FileWriter(jfile);
467      try {
468        jj.write(cb.toString());
469      } finally {
470        jj.close();
471      }
472    }
473  }
474  
475  class CppRecord extends CppCompType {
476    
477    private String fullName;
478    private String name;
479    private String module;
480    private ArrayList<JField<CppType>> fields = 
481      new ArrayList<JField<CppType>>();
482    
483    CppRecord(String name, ArrayList<JField<JType>> flist) {
484      super(name.replaceAll("\\.","::"));
485      this.fullName = name.replaceAll("\\.", "::");
486      int idx = name.lastIndexOf('.');
487      this.name = name.substring(idx+1);
488      this.module = name.substring(0, idx).replaceAll("\\.", "::");
489      for (Iterator<JField<JType>> iter = flist.iterator(); iter.hasNext();) {
490        JField<JType> f = iter.next();
491        fields.add(new JField<CppType>(f.getName(), f.getType().getCppType()));
492      }
493    }
494    
495    String getTypeIDObjectString() {
496      return "new ::hadoop::StructTypeID(" + 
497      fullName + "::getTypeInfo().getFieldTypeInfos())";
498    }
499
500    String genDecl(String fname) {
501      return "  "+name+" "+fname+";\n";
502    }
503    
504    void genSetRTIFilter(CodeBuffer cb) {
505      // we set the RTI filter here
506      cb.append(fullName + "::setTypeFilter(rti.getNestedStructTypeInfo(\""+
507          name + "\"));\n");
508    }
509
510    void genSetupRTIFields(CodeBuffer cb) {
511      cb.append("void " + fullName + "::setupRtiFields() {\n");
512      cb.append("if (NULL == p" + Consts.RTI_FILTER + ") return;\n");
513      cb.append("if (NULL != p" + Consts.RTI_FILTER_FIELDS + ") return;\n");
514      cb.append("p" + Consts.RTI_FILTER_FIELDS + " = new int[p" + 
515          Consts.RTI_FILTER + "->getFieldTypeInfos().size()];\n");
516      cb.append("for (unsigned int " + Consts.RIO_PREFIX + "i=0; " + 
517          Consts.RIO_PREFIX + "i<p" + Consts.RTI_FILTER + 
518          "->getFieldTypeInfos().size(); " + Consts.RIO_PREFIX + "i++) {\n");
519      cb.append("p" + Consts.RTI_FILTER_FIELDS + "[" + Consts.RIO_PREFIX + 
520          "i] = 0;\n");
521      cb.append("}\n");
522      cb.append("for (unsigned int " + Consts.RIO_PREFIX + "i=0; " + 
523          Consts.RIO_PREFIX + "i<p" + Consts.RTI_FILTER + 
524          "->getFieldTypeInfos().size(); " + Consts.RIO_PREFIX + "i++) {\n");
525      cb.append("for (unsigned int " + Consts.RIO_PREFIX + "j=0; " + 
526          Consts.RIO_PREFIX + "j<p" + Consts.RTI_VAR + 
527          "->getFieldTypeInfos().size(); " + Consts.RIO_PREFIX + "j++) {\n");
528      cb.append("if (*(p" + Consts.RTI_FILTER + "->getFieldTypeInfos()[" + 
529          Consts.RIO_PREFIX + "i]) == *(p" + Consts.RTI_VAR + 
530          "->getFieldTypeInfos()[" + Consts.RIO_PREFIX + "j])) {\n");
531      cb.append("p" + Consts.RTI_FILTER_FIELDS + "[" + Consts.RIO_PREFIX + 
532          "i] = " + Consts.RIO_PREFIX + "j+1;\n");
533      cb.append("break;\n");
534      cb.append("}\n");
535      cb.append("}\n");
536      cb.append("}\n");
537      cb.append("}\n");
538    }
539    
540    void genCode(FileWriter hh, FileWriter cc, ArrayList<String> options)
541      throws IOException {
542      CodeBuffer hb = new CodeBuffer();
543      
544      String[] ns = module.split("::");
545      for (int i = 0; i < ns.length; i++) {
546        hb.append("namespace "+ns[i]+" {\n");
547      }
548      
549      hb.append("class "+name+" : public ::hadoop::Record {\n");
550      hb.append("private:\n");
551      
552      for (Iterator<JField<CppType>> i = fields.iterator(); i.hasNext();) {
553        JField<CppType> jf = i.next();
554        String name = jf.getName();
555        CppType type = jf.getType();
556        type.genDecl(hb, name);
557      }
558      
559      // type info vars
560      hb.append("static ::hadoop::RecordTypeInfo* p" + Consts.RTI_VAR + ";\n");
561      hb.append("static ::hadoop::RecordTypeInfo* p" + Consts.RTI_FILTER + ";\n");
562      hb.append("static int* p" + Consts.RTI_FILTER_FIELDS + ";\n");
563      hb.append("static ::hadoop::RecordTypeInfo* setupTypeInfo();\n");
564      hb.append("static void setupRtiFields();\n");
565      hb.append("virtual void deserializeWithoutFilter(::hadoop::IArchive& " + 
566          Consts.RECORD_INPUT + ", const char* " + Consts.TAG + ");\n");
567      hb.append("public:\n");
568      hb.append("static const ::hadoop::RecordTypeInfo& getTypeInfo() " +
569          "{return *p" + Consts.RTI_VAR + ";}\n");
570      hb.append("static void setTypeFilter(const ::hadoop::RecordTypeInfo& rti);\n");
571      hb.append("static void setTypeFilter(const ::hadoop::RecordTypeInfo* prti);\n");
572      hb.append("virtual void serialize(::hadoop::OArchive& " + 
573          Consts.RECORD_OUTPUT + ", const char* " + Consts.TAG + ") const;\n");
574      hb.append("virtual void deserialize(::hadoop::IArchive& " + 
575          Consts.RECORD_INPUT + ", const char* " + Consts.TAG + ");\n");
576      hb.append("virtual const ::std::string& type() const;\n");
577      hb.append("virtual const ::std::string& signature() const;\n");
578      hb.append("virtual bool operator<(const "+name+"& peer_) const;\n");
579      hb.append("virtual bool operator==(const "+name+"& peer_) const;\n");
580      hb.append("virtual ~"+name+"() {};\n");
581      for (Iterator<JField<CppType>> i = fields.iterator(); i.hasNext();) {
582        JField<CppType> jf = i.next();
583        String name = jf.getName();
584        CppType type = jf.getType();
585        type.genGetSet(hb, name);
586      }
587      hb.append("}; // end record "+name+"\n");
588      for (int i=ns.length-1; i>=0; i--) {
589        hb.append("} // end namespace "+ns[i]+"\n");
590      }
591      
592      hh.write(hb.toString());
593      
594      CodeBuffer cb = new CodeBuffer();
595
596      // initialize type info vars
597      cb.append("::hadoop::RecordTypeInfo* " + fullName + "::p" + 
598          Consts.RTI_VAR + " = " + fullName + "::setupTypeInfo();\n");
599      cb.append("::hadoop::RecordTypeInfo* " + fullName + "::p" + 
600          Consts.RTI_FILTER + " = NULL;\n");
601      cb.append("int* " + fullName + "::p" + 
602          Consts.RTI_FILTER_FIELDS + " = NULL;\n\n");
603
604      // setupTypeInfo()
605      cb.append("::hadoop::RecordTypeInfo* "+fullName+"::setupTypeInfo() {\n");
606      cb.append("::hadoop::RecordTypeInfo* p = new ::hadoop::RecordTypeInfo(\"" + 
607          name + "\");\n");
608      for (Iterator<JField<CppType>> i = fields.iterator(); i.hasNext();) {
609        JField<CppType> jf = i.next();
610        String name = jf.getName();
611        CppType type = jf.getType();
612        type.genStaticTypeInfo(cb, name);
613      }
614      cb.append("return p;\n");
615      cb.append("}\n");
616
617      // setTypeFilter()
618      cb.append("void "+fullName+"::setTypeFilter(const " +
619          "::hadoop::RecordTypeInfo& rti) {\n");
620      cb.append("if (NULL != p" + Consts.RTI_FILTER + ") {\n");
621      cb.append("delete p" + Consts.RTI_FILTER + ";\n");
622      cb.append("}\n");
623      cb.append("p" + Consts.RTI_FILTER + " = new ::hadoop::RecordTypeInfo(rti);\n");
624      cb.append("if (NULL != p" + Consts.RTI_FILTER_FIELDS + ") {\n");
625      cb.append("delete p" + Consts.RTI_FILTER_FIELDS + ";\n");
626      cb.append("}\n");
627      cb.append("p" + Consts.RTI_FILTER_FIELDS + " = NULL;\n");
628      // set RTIFilter for nested structs. We may end up with multiple lines that 
629      // do the same thing, if the same struct is nested in more than one field, 
630      // but that's OK. 
631      for (Iterator<JField<CppType>> i = fields.iterator(); i.hasNext();) {
632        JField<CppType> jf = i.next();
633        CppType type = jf.getType();
634        type.genSetRTIFilter(cb);
635      }
636      cb.append("}\n");
637      
638      // setTypeFilter()
639      cb.append("void "+fullName+"::setTypeFilter(const " +
640          "::hadoop::RecordTypeInfo* prti) {\n");
641      cb.append("if (NULL != prti) {\n");
642      cb.append("setTypeFilter(*prti);\n");
643      cb.append("}\n");
644      cb.append("}\n");
645
646      // setupRtiFields()
647      genSetupRTIFields(cb);
648
649      // serialize()
650      cb.append("void "+fullName+"::serialize(::hadoop::OArchive& " + 
651          Consts.RECORD_OUTPUT + ", const char* " + Consts.TAG + ") const {\n");
652      cb.append(Consts.RECORD_OUTPUT + ".startRecord(*this," + 
653          Consts.TAG + ");\n");
654      for (Iterator<JField<CppType>> i = fields.iterator(); i.hasNext();) {
655        JField<CppType> jf = i.next();
656        String name = jf.getName();
657        CppType type = jf.getType();
658        if (type instanceof JBuffer.CppBuffer) {
659          cb.append(Consts.RECORD_OUTPUT + ".serialize("+name+","+name+
660              ".length(),\""+name+"\");\n");
661        } else {
662          cb.append(Consts.RECORD_OUTPUT + ".serialize("+name+",\""+
663              name+"\");\n");
664        }
665      }
666      cb.append(Consts.RECORD_OUTPUT + ".endRecord(*this," + Consts.TAG + ");\n");
667      cb.append("return;\n");
668      cb.append("}\n");
669      
670      // deserializeWithoutFilter()
671      cb.append("void "+fullName+"::deserializeWithoutFilter(::hadoop::IArchive& " +
672          Consts.RECORD_INPUT + ", const char* " + Consts.TAG + ") {\n");
673      cb.append(Consts.RECORD_INPUT + ".startRecord(*this," + 
674          Consts.TAG + ");\n");
675      for (Iterator<JField<CppType>> i = fields.iterator(); i.hasNext();) {
676        JField<CppType> jf = i.next();
677        String name = jf.getName();
678        CppType type = jf.getType();
679        if (type instanceof JBuffer.CppBuffer) {
680          cb.append("{\nsize_t len=0; " + Consts.RECORD_INPUT + ".deserialize("+
681              name+",len,\""+name+"\");\n}\n");
682        } else {
683          cb.append(Consts.RECORD_INPUT + ".deserialize("+name+",\""+
684              name+"\");\n");
685        }
686      }
687      cb.append(Consts.RECORD_INPUT + ".endRecord(*this," + Consts.TAG + ");\n");
688      cb.append("return;\n");
689      cb.append("}\n");
690      
691      // deserialize()
692      cb.append("void "+fullName+"::deserialize(::hadoop::IArchive& " +
693          Consts.RECORD_INPUT + ", const char* " + Consts.TAG + ") {\n");
694      cb.append("if (NULL == p" + Consts.RTI_FILTER + ") {\n");
695      cb.append("deserializeWithoutFilter(" + Consts.RECORD_INPUT + ", " + 
696          Consts.TAG + ");\n");
697      cb.append("return;\n");
698      cb.append("}\n");
699      cb.append("// if we're here, we need to read based on version info\n");
700      cb.append(Consts.RECORD_INPUT + ".startRecord(*this," + 
701          Consts.TAG + ");\n");
702      cb.append("setupRtiFields();\n");
703      cb.append("for (unsigned int " + Consts.RIO_PREFIX + "i=0; " + 
704          Consts.RIO_PREFIX + "i<p" + Consts.RTI_FILTER + 
705          "->getFieldTypeInfos().size(); " + Consts.RIO_PREFIX + "i++) {\n");
706      int ct = 0;
707      for (Iterator<JField<CppType>> i = fields.iterator(); i.hasNext();) {
708        JField<CppType> jf = i.next();
709        String name = jf.getName();
710        CppType type = jf.getType();
711        ct++;
712        if (1 != ct) {
713          cb.append("else ");
714        }
715        cb.append("if (" + ct + " == p" + Consts.RTI_FILTER_FIELDS + "[" +
716            Consts.RIO_PREFIX + "i]) {\n");
717        if (type instanceof JBuffer.CppBuffer) {
718          cb.append("{\nsize_t len=0; " + Consts.RECORD_INPUT + ".deserialize("+
719              name+",len,\""+name+"\");\n}\n");
720        } else {
721          cb.append(Consts.RECORD_INPUT + ".deserialize("+name+",\""+
722              name+"\");\n");
723        }
724        cb.append("}\n");
725      }
726      if (0 != ct) {
727        cb.append("else {\n");
728        cb.append("const std::vector< ::hadoop::FieldTypeInfo* >& typeInfos = p" + 
729            Consts.RTI_FILTER + "->getFieldTypeInfos();\n");
730        cb.append("::hadoop::Utils::skip(" + Consts.RECORD_INPUT + 
731            ", typeInfos[" + Consts.RIO_PREFIX + "i]->getFieldID()->c_str()" + 
732            ", *(typeInfos[" + Consts.RIO_PREFIX + "i]->getTypeID()));\n");
733        cb.append("}\n");
734      }
735      cb.append("}\n");
736      cb.append(Consts.RECORD_INPUT + ".endRecord(*this, " + Consts.TAG+");\n");
737      cb.append("}\n");
738
739      // operator <
740      cb.append("bool "+fullName+"::operator< (const "+fullName+"& peer_) const {\n");
741      cb.append("return (1\n");
742      for (Iterator<JField<CppType>> i = fields.iterator(); i.hasNext();) {
743        JField<CppType> jf = i.next();
744        String name = jf.getName();
745        cb.append("&& ("+name+" < peer_."+name+")\n");
746      }
747      cb.append(");\n");
748      cb.append("}\n");
749      
750      cb.append("bool "+fullName+"::operator== (const "+fullName+"& peer_) const {\n");
751      cb.append("return (1\n");
752      for (Iterator<JField<CppType>> i = fields.iterator(); i.hasNext();) {
753        JField<CppType> jf = i.next();
754        String name = jf.getName();
755        cb.append("&& ("+name+" == peer_."+name+")\n");
756      }
757      cb.append(");\n");
758      cb.append("}\n");
759      
760      cb.append("const ::std::string&"+fullName+"::type() const {\n");
761      cb.append("static const ::std::string type_(\""+name+"\");\n");
762      cb.append("return type_;\n");
763      cb.append("}\n");
764      
765      cb.append("const ::std::string&"+fullName+"::signature() const {\n");
766      cb.append("static const ::std::string sig_(\""+getSignature()+"\");\n");
767      cb.append("return sig_;\n");
768      cb.append("}\n");
769      
770      cc.write(cb.toString());
771    }
772  }
773  
774  class CRecord extends CCompType {
775    
776  }
777  
778  private String signature;
779  
780  /**
781   * Creates a new instance of JRecord
782   */
783  public JRecord(String name, ArrayList<JField<JType>> flist) {
784    setJavaType(new JavaRecord(name, flist));
785    setCppType(new CppRecord(name, flist));
786    setCType(new CRecord());
787    // precompute signature
788    int idx = name.lastIndexOf('.');
789    String recName = name.substring(idx+1);
790    StringBuilder sb = new StringBuilder();
791    sb.append("L").append(recName).append("(");
792    for (Iterator<JField<JType>> i = flist.iterator(); i.hasNext();) {
793      String s = i.next().getType().getSignature();
794      sb.append(s);
795    }
796    sb.append(")");
797    signature = sb.toString();
798  }
799  
800  String getSignature() {
801    return signature;
802  }
803  
804  void genCppCode(FileWriter hh, FileWriter cc, ArrayList<String> options)
805    throws IOException {
806    ((CppRecord)getCppType()).genCode(hh, cc, options);
807  }
808  
809  void genJavaCode(String destDir, ArrayList<String> options)
810    throws IOException {
811    ((JavaRecord)getJavaType()).genCode(destDir, options);
812  }
813}