private List<FieldDeclaration> prepareFields() {
      List<FieldDeclaration> fields = Lists.create();

      // shufles
      for (ShuffleModel.Segment segment : shuffle.getSegments()) {
        SimpleName shuffleName = names.create("shuffle");
        shuffleNames.put(segment, shuffleName);
        Name shuffleTypeName = segment.getCompiled().getCombineOutputType().getQualifiedName();
        fields.add(
            factory.newFieldDeclaration(
                null,
                new AttributeBuilder(factory).Private().toAttributes(),
                importer.toType(shuffleTypeName),
                shuffleName,
                null));
      }

      // rendezvous
      for (ReduceUnit unit : reduceUnits) {
        if (unit.canCombine() == false) {
          continue;
        }
        Fragment first = unit.getFragments().get(0);
        SimpleName rendezvousName = names.create("combine");
        rendezvousNames.put(first, rendezvousName);
        fields.add(
            factory.newFieldDeclaration(
                null,
                new AttributeBuilder(factory).Private().toAttributes(),
                importer.toType(first.getCompiled().getQualifiedName()),
                rendezvousName,
                null));
      }
      return fields;
    }
    private MethodDeclaration createGetRendezvous() {
      Map<FlowElement, SimpleName> fragments = Maps.create();
      for (Map.Entry<Fragment, SimpleName> entry : rendezvousNames.entrySet()) {
        fragments.put(entry.getKey().getFactors().get(0).getElement(), entry.getValue());
      }

      List<Statement> cases = Lists.create();
      for (List<ShuffleModel.Segment> group : ShuffleEmiterUtil.groupByElement(shuffle)) {
        for (ShuffleModel.Segment segment : group) {
          cases.add(factory.newSwitchCaseLabel(v(segment.getPortId())));
        }
        FlowElement element = group.get(0).getPort().getOwner();
        SimpleName rendezvousName = fragments.get(element);
        if (rendezvousName == null) {
          cases.add(
              new ExpressionBuilder(factory, Models.toNullLiteral(factory)).toReturnStatement());
        } else {
          cases.add(
              new ExpressionBuilder(factory, factory.newThis())
                  .field(rendezvousName)
                  .toReturnStatement());
        }
      }
      cases.add(factory.newSwitchDefaultLabel());
      cases.add(new TypeBuilder(factory, t(AssertionError.class)).newObject().toThrowStatement());

      SimpleName argument = names.create("nextKey");
      List<Statement> statements = Lists.create();
      statements.add(
          factory.newSwitchStatement(
              new ExpressionBuilder(factory, argument)
                  .method(SegmentedWritable.ID_GETTER)
                  .toExpression(),
              cases));

      return factory.newMethodDeclaration(
          null,
          new AttributeBuilder(factory).annotation(t(Override.class)).Protected().toAttributes(),
          importer.resolve(
              factory.newParameterizedType(
                  Models.toType(factory, Rendezvous.class),
                  importer.toType(shuffle.getCompiled().getValueTypeName()))),
          factory.newSimpleName(SegmentedReducer.GET_RENDEZVOUS),
          Collections.singletonList(
              factory.newFormalParameterDeclaration(
                  importer.toType(shuffle.getCompiled().getKeyTypeName()), argument)),
          statements);
    }
 Engine(FlowCompilingEnvironment environment, StageModel model) {
   assert environment != null;
   assert model != null;
   this.reduceUnits = model.getReduceUnits();
   this.shuffle = model.getShuffleModel();
   this.factory = environment.getModelFactory();
   Name packageName = environment.getStagePackageName(model.getStageBlock().getStageNumber());
   this.importer =
       new ImportBuilder(
           factory,
           factory.newPackageDeclaration(packageName),
           ImportBuilder.Strategy.TOP_LEVEL);
   this.names = new NameGenerator(factory);
   this.context = names.create("context");
   this.shuffleNames = Maps.create();
   this.rendezvousNames = Maps.create();
 }