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