SimpleAtom.java

/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed 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 androidx.test.espresso.web.model;

import static com.google.common.base.Preconditions.checkNotNull;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.annotation.Nullable;

/** A simple implementation of Atom suitable for subclassing. */
public class SimpleAtom implements Atom<Evaluation> {

  private final String script;
  private final ElementReferencePlacement elementPlacement;

  /** Controls whether the ElementReference appears as the first arg or last arg to the script. */
  public enum ElementReferencePlacement {
    FIRST,
    LAST
  };

  /**
   * Creates a SimpleAtom which runs the given script and places any ElementReference as the first
   * argument to the script.
   */
  public SimpleAtom(String script) {
    this(script, ElementReferencePlacement.FIRST);
  }

  /**
   * Creates a SimpleAtom which runs the given script and places any supplied ElementReference
   * either as the first or last argument to the script.
   */
  public SimpleAtom(String script, ElementReferencePlacement elementPlacement) {
    this.script = checkNotNull(script);
    this.elementPlacement = checkNotNull(elementPlacement);
  }

  /** Returns the script this SimpleAtom was created with. */
  @Override
  public final String getScript() {
    return script;
  }

  /**
   * The SimpleAtom's transform method checks the Evaluation object for success.
   *
   * <p>If the Evaluation object has an error, the default behaviour is to throw an exception,
   * subclasses may change this behaviour via the handleBadEvaluation method.
   */
  @Override
  public final Evaluation transform(Evaluation e) {
    if (e.getStatus() != 0) {
      return checkNotNull(handleBadEvaluation(e), "Evaluation bad and handler returned null! " + e);
    }
    return e;
  }

  /**
   * The SimpleAtom presents an argument list to the script which follows some basic conventions.
   *
   * <p>If an ElementReference is provided, it is placed either in the first or last position based
   * on the ElementReferencePlacement provided to the constructor.
   *
   * <p>The nonContextualArguments (if provided via getNonContextualArguments) will appear after the
   * ElementReference.
   */
  @Override
  public final List<Object> getArguments(@Nullable ElementReference elementRef) {
    List<Object> nonContextualArguments = checkNotNull(getNonContextualArguments());
    if (null == elementRef) {
      handleNoElementReference();
    }

    if (nonContextualArguments.size() == 0 && null == elementRef) {
      return Collections.EMPTY_LIST;
    } else {
      if (null == elementRef) {
        return nonContextualArguments;
      } else {
        List<Object> args = new ArrayList<Object>(nonContextualArguments.size() + 1);
        if (elementPlacement == ElementReferencePlacement.FIRST) {
          args.add(elementRef);
          args.addAll(nonContextualArguments);
        } else {
          args.addAll(nonContextualArguments);
          args.add(elementRef);
        }
        return args;
      }
    }
  }

  /**
   * Extend this method to handle the case where getArguments() has been called without an
   * ElementReference. Implementors may want to throw an exception here if they require an
   * ElementReference to evaluate properly.
   */
  protected void handleNoElementReference() {}

  /**
   * Extend this method to pass additional arguments to the script.
   *
   * @return a list of arguments (non-null)
   */
  protected List<Object> getNonContextualArguments() {
    return Collections.EMPTY_LIST;
  }

  /**
   * Extend this method to handle a failure code in the Evaluation object.
   *
   * <p>The default implementation will throw an exception, subclasses may want to ignore certain
   * failure cases.
   *
   * @return Evaluation the evaluation object (must be non-null)
   * @throws RuntimeException if the badness level is too high.
   */
  protected Evaluation handleBadEvaluation(Evaluation e) {
    throw new RuntimeException("Error in evaluation" + e);
  }
}