/// <summary>
 /// Copy a custom WrappedMapper.Context, optionally replacing
 /// the input and output.
 /// </summary>
 /// <?/>
 /// <?/>
 /// <?/>
 /// <?/>
 /// <param name="context">the context to clone</param>
 /// <param name="conf">a new configuration</param>
 /// <param name="reader">Reader to read from. Null means to clone from context.</param>
 /// <param name="writer">Writer to write to. Null means to clone from context.</param>
 /// <returns>a new context. it will not be the same class as the original.</returns>
 /// <exception cref="System.IO.IOException"/>
 /// <exception cref="System.Exception"/>
 public static Mapper.Context CloneMapContext <K1, V1, K2, V2>(MapContext <K1, V1, K2
                                                                           , V2> context, Configuration conf, RecordReader <K1, V1> reader, RecordWriter <K2,
                                                                                                                                                          V2> writer)
 {
     try
     {
         // get the outer object pointer
         object outer = OuterMapField.GetValue(context);
         // if it is a wrapped 21 context, unwrap it
         if ("org.apache.hadoop.mapreduce.lib.map.WrappedMapper$Context".Equals(context.GetType
                                                                                    ().FullName))
         {
             context = (MapContext <K1, V1, K2, V2>)WrappedContextField.GetValue(context);
         }
         // if the reader or writer aren't given, use the same ones
         if (reader == null)
         {
             reader = (RecordReader <K1, V1>)ReaderField.GetValue(context);
         }
         if (writer == null)
         {
             writer = (RecordWriter <K2, V2>)WriterField.GetValue(context);
         }
         if (useV21)
         {
             object basis = MapContextImplConstructor.NewInstance(conf, context.GetTaskAttemptID
                                                                      (), reader, writer, context.GetOutputCommitter(), ReporterField.GetValue(context
                                                                                                                                               ), context.GetInputSplit());
             return((Mapper.Context)MapContextConstructor.NewInstance(outer, basis));
         }
         else
         {
             return((Mapper.Context)MapContextConstructor.NewInstance(outer, conf, context.GetTaskAttemptID
                                                                          (), reader, writer, context.GetOutputCommitter(), ReporterField.GetValue(context
                                                                                                                                                   ), context.GetInputSplit()));
         }
     }
     catch (MemberAccessException e)
     {
         throw new ArgumentException("Can't access field", e);
     }
     catch (InstantiationException e)
     {
         throw new ArgumentException("Can't create object", e);
     }
     catch (TargetInvocationException e)
     {
         throw new ArgumentException("Can't invoke constructor", e);
     }
 }