/*
 * Copyright (C) 2011 Google 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.google.caliper.util;

import com.google.common.collect.ImmutableList;
import com.google.common.primitives.Primitives;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.util.List;

public class Parsers {
  public static final Parser<String> IDENTITY =
      new Parser<String>() {
        @Override
        public String parse(CharSequence in) {
          return in.toString();
        }
      };

  private static final List<String> CONVERSION_METHOD_NAMES =
      ImmutableList.of("fromString", "decode", "valueOf");

  /**
   * Parser that tries, in this order:
   *
   * <ul>
   * <li>ResultType.fromString(String)
   * <li>ResultType.decode(String)
   * <li>ResultType.valueOf(String)
   * <li>new ResultType(String)
   * </ul>
   */
  public static <T> Parser<T> conventionalParser(Class<T> resultType) throws NoSuchMethodException {
    if (resultType == String.class) {
      @SuppressWarnings("unchecked") // T == String
      Parser<T> identity = (Parser<T>) IDENTITY;
      return identity;
    }

    final Class<T> wrappedResultType = Primitives.wrap(resultType);

    for (String methodName : CONVERSION_METHOD_NAMES) {
      try {
        final Method method = wrappedResultType.getDeclaredMethod(methodName, String.class);

        if (Util.isStatic(method) && wrappedResultType.isAssignableFrom(method.getReturnType())) {
          method.setAccessible(true); // to permit inner enums, etc.
          return new InvokingParser<T>() {
            @Override
            protected T invoke(String input) throws Exception {
              return wrappedResultType.cast(method.invoke(null, input));
            }
          };
        }
      } catch (Exception tryAgain) {
      }
    }

    final Constructor<T> constr = wrappedResultType.getDeclaredConstructor(String.class);
    constr.setAccessible(true);
    return new InvokingParser<T>() {
      @Override
      protected T invoke(String input) throws Exception {
        return wrappedResultType.cast(constr.newInstance(input));
      }
    };
  }

  abstract static class InvokingParser<T> implements Parser<T> {
    @Override
    public T parse(CharSequence input) throws ParseException {
      try {
        return invoke(input.toString());
      } catch (InvocationTargetException e) {
        Throwable cause = e.getCause();
        String desc = firstNonNull(cause.getMessage(), cause.getClass().getSimpleName());
        throw newParseException(desc, cause);
      } catch (Exception e) {
        throw newParseException("Unknown parsing problem", e);
      }
    }

    protected abstract T invoke(String input) throws Exception;
  }

  public static ParseException newParseException(String message, Throwable cause) {
    ParseException pe = newParseException(message);
    pe.initCause(cause);
    return pe;
  }

  public static ParseException newParseException(String message) {
    return new ParseException(message, 0);
  }

  private static <T> T firstNonNull(T first, T second) {
    return (first != null) ? first : second;
  }
}

@@ -878,31 +878,8 @@
 ion;
-%0Aimport java.util.List;
 %0A%0Apu
@@ -1116,16 +1116,25 @@
 c final 
+Immutable
 List%3CStr

Replace constants (static final CONSTANT_CASE) declaration type which use the general collection interface (e.g. List) with an immutable type (e.g. ImmutableList).