/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.beam.runners.core;

import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.apache.beam.sdk.coders.Coder;
import org.apache.beam.sdk.transforms.DoFn.Context;
import org.apache.beam.sdk.transforms.windowing.BoundedWindow;
import org.apache.beam.sdk.util.WindowedValue;
import org.apache.beam.sdk.values.TupleTag;

/**
 * Base class for implementations of {@link ExecutionContext}.
 *
 * <p>A concrete subclass should implement {@link #createStepContext} to create the appropriate
 * {@link StepContext} implementation. Any {@code StepContext} created will
 * be cached for the lifetime of this {@link ExecutionContext}.
 *
 * <p>BaseExecutionContext is generic to allow implementing subclasses to return a concrete subclass
 * of {@link StepContext} from {@link #getOrCreateStepContext(String, String)} and
 * {@link #getAllStepContexts()} without forcing each subclass to override the method, e.g.
 * <pre>{@code
 * {@literal @}Override
 * StreamingModeExecutionContext.StepContext getOrCreateStepContext(...) {
 *   return (StreamingModeExecutionContext.StepContext) super.getOrCreateStepContext(...);
 * }
 * }</pre>
 *
 * <p>When a subclass of {@code BaseExecutionContext} has been downcast, the return types of
 * {@link #createStepContext(String, String)},
 * {@link #getOrCreateStepContext(String, String)}, and {@link #getAllStepContexts()}
 * will be appropriately specialized.
 */
public abstract class BaseExecutionContext<T extends ExecutionContext.StepContext>
    implements ExecutionContext {

  private Map<String, T> cachedStepContexts = new HashMap<>();

  /**
   * Implementations should override this to create the specific type
   * of {@link StepContext} they need.
   */
  protected abstract T createStepContext(String stepName, String transformName);

  /**
   * Returns the {@link StepContext} associated with the given step.
   */
  @Override
  public T getOrCreateStepContext(String stepName, String transformName) {
    final String finalStepName = stepName;
    final String finalTransformName = transformName;
    return getOrCreateStepContext(
        stepName,
        new CreateStepContextFunction<T>() {
          @Override
          public T create() {
            return createStepContext(finalStepName, finalTransformName);
          }
        });
  }

  /**
   * Factory method interface to create an execution context if none exists during
   * {@link #getOrCreateStepContext(String, CreateStepContextFunction)}.
   */
  protected interface CreateStepContextFunction<T extends ExecutionContext.StepContext> {
    T create();
  }

  protected final T getOrCreateStepContext(String stepName,
      CreateStepContextFunction<T> createContextFunc) {
    T context = cachedStepContexts.get(stepName);
    if (context == null) {
      context = createContextFunc.create();
      cachedStepContexts.put(stepName, context);
    }

    return context;
  }

  /**
   * Returns a collection view of all of the {@link StepContext}s.
   */
  @Override
  public Collection<? extends T> getAllStepContexts() {
    return Collections.unmodifiableCollection(cachedStepContexts.values());
  }

  /**
   * Hook for subclasses to implement that will be called whenever
   * {@link Context#output(Object)} is called.
   */
  @Override
  public void noteOutput(WindowedValue<?> output) {}

  /**
   * Hook for subclasses to implement that will be called whenever
   * {@link Context#output(TupleTag, Object)} is called.
   */
  @Override
  public void noteOutput(TupleTag<?> tag, WindowedValue<?> output) {}

  /**
   * Base class for implementations of {@link ExecutionContext.StepContext}.
   *
   * <p>To complete a concrete subclass, implement {@link #timerInternals} and
   * {@link #stateInternals}.
   */
  public abstract static class StepContext implements ExecutionContext.StepContext {
    private final ExecutionContext executionContext;
    private final String stepName;
    private final String transformName;

    public StepContext(ExecutionContext executionContext, String stepName, String transformName) {
      this.executionContext = executionContext;
      this.stepName = stepName;
      this.transformName = transformName;
    }

    @Override
    public String getStepName() {
      return stepName;
    }

    @Override
    public String getTransformName() {
      return transformName;
    }

    @Override
    public void noteOutput(WindowedValue<?> output) {
      executionContext.noteOutput(output);
    }

    @Override
    public void noteOutput(TupleTag<?> tag, WindowedValue<?> output) {
      executionContext.noteOutput(tag, output);
    }

    @Override
    public <T, W extends BoundedWindow> void writePCollectionViewData(
        TupleTag<?> tag,
        Iterable<WindowedValue<T>> data, Coder<Iterable<WindowedValue<T>>> dataCoder,
        W window, Coder<W> windowCoder) throws IOException {
      throw new UnsupportedOperationException("Not implemented.");
    }

    @Override
    public abstract StateInternals stateInternals();

    @Override
    public abstract TimerInternals timerInternals();
  }
}

@@ -942,16 +942,22 @@
 va.util.
+Linked
 HashMap;
@@ -2472,16 +2472,22 @@
 s = new 
+Linked
 HashMap%3C

Use LinkedHashMap for step contexts in BaseExecutionContext