/*
 * Copyright (C) 2017 Square, Inc.
 *
 * 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 com.squareup.moshi.recipes;

import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonDataException;
import com.squareup.moshi.JsonReader;
import com.squareup.moshi.JsonWriter;
import com.squareup.moshi.Moshi;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Set;
import javax.annotation.Nullable;

public final class DefaultOnDataMismatchAdapter<T> extends JsonAdapter<T> {
  private final JsonAdapter<T> delegate;
  private final T defaultValue;

  private DefaultOnDataMismatchAdapter(JsonAdapter<T> delegate, T defaultValue) {
    this.delegate = delegate;
    this.defaultValue = defaultValue;
  }

  @Override public T fromJson(JsonReader reader) throws IOException {
    // Read the value first so that the reader will be in a known state even if there's an
    // exception. Otherwise it may be awkward to recover: it might be between calls to
    // beginObject() and endObject() for example.
    Object jsonValue = reader.readJsonValue();

    // Use the delegate to convert the JSON value to the target type.
    try {
      return delegate.fromJsonValue(jsonValue);
    } catch (JsonDataException e) {
      return defaultValue;
    }
  }

  @Override public void toJson(JsonWriter writer, T value) throws IOException {
    delegate.toJson(writer, value);
  }

  public static <T> Factory newFactory(final Class<T> type, final T defaultValue) {
    return new Factory() {
      @Override public @Nullable JsonAdapter<?> create(
          Type requestedType, Set<? extends Annotation> annotations, Moshi moshi) {
        if (type != requestedType) return null;
        JsonAdapter<T> delegate = moshi.nextAdapter(this, type, annotations);
        return new DefaultOnDataMismatchAdapter<>(delegate, defaultValue);
      }
    };
  }
}

@@ -1366,36 +1366,36 @@
  // 
-Read the value first so that
+Use a peeked reader to leave
  the
@@ -1406,16 +1406,8 @@
 der 
-will be 
 in a
@@ -1437,23 +1437,16 @@
 ere's an
-%0A    //
  excepti
@@ -1452,255 +1452,145 @@
 ion.
- Otherwise it may be awkward to recover: it might be between calls to%0A    // beginObject() and endObject() for example.%0A    Object jsonValue = reader.readJsonValue();%0A%0A    // Use the delegate to convert the JSON value to the target type.%0A    try %7B
+%0A    JsonReader peeked = reader.peekJson();%0A    T result;%0A    try %7B%0A      // Attempt to decode to the target type with the peeked reader.
 %0A   
@@ -1594,20 +1594,22 @@
       re
-turn
+sult =
  delegat
@@ -1622,23 +1622,15 @@
 Json
-Value(jsonValue
+(peeked
 );%0A 
@@ -1672,20 +1672,22 @@
       re
-turn
+sult =
  default
@@ -1698,16 +1698,181 @@
 e;%0A    %7D
+ finally %7B%0A      peeked.close();%0A    %7D%0A    // Skip the value back on the reader, no matter the state of the peeked reader.%0A    reader.skipValue();%0A    return result;
 %0A  %7D%0A%0A  

Use peekJson in the DefaultOnDataMismatchAdapter example. (#809)