Evaluation.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.checkState;

import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
import com.google.common.base.Objects;
import java.util.Map;
import javax.annotation.Nullable;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONStringer;
import org.json.JSONTokener;

/** Represents the results of a Javascript execution. */
public final class Evaluation implements JSONAble, Parcelable {
  private static final String STATUS_KEY = "status";
  private static final String MESSAGE_KEY = "message";
  private static final String VALUE_KEY = "value";

  private int status;
  private boolean hasMessage;
  private String message;
  private Object value;

  private Evaluation(Builder b) {
    this.status = b.status;
    this.value = b.value;
    this.hasMessage = b.hasMessage;
    this.message = b.message;
  }

  protected Evaluation(Parcel in) {
    readFromParcel(in);
  }

  @Override
  public int hashCode() {
    return Objects.hashCode(status, value, hasMessage, message);
  }

  @Override
  public boolean equals(Object o) {
    if (o instanceof Evaluation) {
      Evaluation other = (Evaluation) o;
      if (other.status == status) {
        if (hasMessage == other.hasMessage) {
          if (hasMessage) {
            return message.equals(other.message);
          } else {
            if (value == null) {
              return other.value == null;
            } else {
              return value.equals(other.value);
            }
          }
        }
      }
    }
    return false;
  }

  public int getStatus() {
    return status;
  }

  @Nullable
  public Object getValue() {
    return value;
  }

  public boolean hasMessage() {
    return hasMessage;
  }

  public String getMessage() {
    checkState(hasMessage);
    return message;
  }

  @Override
  public String toJSONString() {
    try {
      JSONStringer stringer =
          new JSONStringer().object().key(STATUS_KEY).value(status).key(VALUE_KEY);
      if ((value instanceof String)
          || (value instanceof Number)
          || (value instanceof Boolean)
          || (value == null)) {
        stringer.value(value);
      } else {
        String jsonValue = ModelCodec.encode(value);
        stringer.value(new JSONTokener(jsonValue).nextValue());
      }
      stringer.endObject();
      return stringer.toString();
    } catch (JSONException je) {
      throw new RuntimeException(je);
    }
  }

  static final JSONAble.DeJSONFactory DEJSONIZER =
      new JSONAble.DeJSONFactory() {
        @Override
        public Object attemptDeJSONize(Map<String, Object> map) {
          if (map.size() == 2) {
            Object maybeStatus = map.get(STATUS_KEY);
            if (maybeStatus instanceof Integer) {
              Object maybeValue = map.get(VALUE_KEY);
              if (null != maybeValue) {
                Evaluation.Builder builder =
                    new Evaluation.Builder()
                        .setStatus((Integer) maybeStatus)
                        .setValue(maybeValue == JSONObject.NULL ? null : maybeValue);
                if (maybeValue instanceof Map) {
                  Map mapValue = (Map) maybeValue;
                  if (mapValue.size() == 1) {
                    Object maybeMessage = mapValue.get(MESSAGE_KEY);
                    if (maybeMessage instanceof String) {
                      builder.setMessage((String) maybeMessage);
                    } else if (maybeMessage == JSONObject.NULL) {
                      builder.setMessage(null);
                    }
                  }
                }
                return builder.build();
              }
            }
          }
          return null;
        }
      };

  @Override
  public String toString() {
    return String.format(
        "Evaluation: status: %d value: %s hasMessage: %s message: %s",
        status, value, hasMessage, message);
  }

  @Override
  public int describeContents() {
    return 0;
  }

  @Override
  public void writeToParcel(@NonNull Parcel dest, int flags) {
    dest.writeString(ModelCodec.encode(this));
  }

  public void readFromParcel(Parcel in) {
    Evaluation evaluation = ModelCodec.decodeEvaluation(in.readString());
    status = evaluation.status;
    hasMessage = evaluation.hasMessage;
    message = evaluation.message;
    value = evaluation.value;
  }

  public static final Creator<Evaluation> CREATOR =
      new Parcelable.Creator<Evaluation>() {
        @Override
        public Evaluation createFromParcel(Parcel in) {
          return new Evaluation(in);
        }

        @Override
        public Evaluation[] newArray(int size) {
          return new Evaluation[size];
        }
      };

  static class Builder {
    private Object value;
    private int status;
    private boolean hasMessage;
    private String message;

    public Builder setMessage(String message) {
      this.message = message;
      hasMessage = true;
      return this;
    }

    public Builder setValue(Object value) {
      this.value = value;
      return this;
    }

    public Builder setStatus(int status) {
      this.status = status;
      return this;
    }

    public Evaluation build() {
      return new Evaluation(this);
    }
  }
}