/*
 * Copyright 2008 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.template.soy.data;

import com.google.template.soy.data.restricted.BooleanData;
import com.google.template.soy.data.restricted.FloatData;
import com.google.template.soy.data.restricted.IntegerData;
import com.google.template.soy.data.restricted.NullData;
import com.google.template.soy.data.restricted.StringData;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

/**
 * Abstract base class for all nodes in a Soy data tree.
 *
 * <p>Important: Even though this class is not marked 'final', do not extend this class.
 *
 */
public abstract class SoyData extends SoyAbstractValue {

  /**
   * Creates deprecated SoyData objects from standard Java data structures.
   *
   * @param obj The existing object or data structure to convert.
   * @return A SoyData object or tree that corresponds to the given object.
   * @throws SoyDataException If the given object cannot be converted to SoyData.
   * @deprecated It's best to pass whatever object you have directly to the Soy templates you're
   *     using -- Soy understands primitives, lists, and maps natively, and if you install runtime
   *     support you can also pass protocol buffers. If you're interacting directly with the Soy
   *     runtime and need SoyValue objects, use SoyValueConverter instead.
   */
  @Deprecated
  public static SoyData createFromExistingData(Object obj) {

    // Important: This is frozen for backwards compatibility, For future changes (pun not intended),
    // use SoyValueConverter, which works with the new interfaces SoyValue and SoyValueProvider.

    if (obj == null) {
      return NullData.INSTANCE;
    } else if (obj instanceof SoyData) {
      return (SoyData) obj;
    } else if (obj instanceof String) {
      return StringData.forValue((String) obj);
    } else if (obj instanceof Boolean) {
      return BooleanData.forValue((Boolean) obj);
    } else if (obj instanceof Integer) {
      return IntegerData.forValue((Integer) obj);
    } else if (obj instanceof Long) {
      return IntegerData.forValue((Long) obj);
    } else if (obj instanceof Map<?, ?>) {
      @SuppressWarnings("unchecked")
      Map<String, ?> objCast = (Map<String, ?>) obj;
      return new SoyMapData(objCast);
    } else if (obj instanceof Iterable<?>) {
      return new SoyListData((Iterable<?>) obj);
    } else if (obj instanceof Double) {
      return FloatData.forValue((Double) obj);
    } else if (obj instanceof Float) {
      // Automatically convert float to double.
      return FloatData.forValue((Float) obj);
    } else if (obj instanceof Future<?>) {
      // Note: In the old SoyData, we don't support late-resolution of Futures. We immediately
      // resolve the Future object here. For late-resolution, use SoyValueConverter.convert().
      try {
        return createFromExistingData(((Future<?>) obj).get());
      } catch (InterruptedException e) {
        throw new SoyDataException(
            "Encountered InterruptedException when resolving Future object.", e);
      } catch (ExecutionException e) {
        throw new SoyDataException(
            "Encountered ExecutionException when resolving Future object.", e);
      }
    } else {
      throw new SoyDataException(
          "Attempting to convert unrecognized object to Soy data (object type "
              + obj.getClass().getName()
              + ").");
    }
  }
}

@@ -632,302 +632,8 @@
 a;%0A%0A
-import com.google.template.soy.data.restricted.BooleanData;%0Aimport com.google.template.soy.data.restricted.FloatData;%0Aimport com.google.template.soy.data.restricted.IntegerData;%0Aimport com.google.template.soy.data.restricted.NullData;%0Aimport com.google.template.soy.data.restricted.StringData;%0A
 impo
@@ -1717,685 +1717,68 @@
 ) %7B%0A
-%0A
     
-// Important: This is frozen for backwards compatibility, For future changes (pun not intended),%0A    // use SoyValueConverter, which works with the new interfaces SoyValue and SoyValueProvider.%0A%0A    if (obj == null) %7B%0A      return NullData.INSTANCE;%0A    %7D else if (obj instanceof SoyData) %7B%0A      return (SoyData) obj;%0A    %7D else if (obj instanceof String) %7B%0A      return StringData.forValue((String) obj);%0A    %7D else if (obj instanceof Boolean) %7B%0A      return BooleanData.forValue((Boolean) obj);%0A    %7D else if (obj instanceof Integer) %7B%0A      return IntegerData.forValue((Integer) obj);%0A    %7D else if (obj instanceof Long) %7B%0A      return IntegerData.forValue((Long) obj)
+if (obj instanceof SoyData) %7B%0A      return (SoyData) obj
 ;%0A  
@@ -2044,228 +2044,8 @@
 j);%0A
-    %7D else if (obj instanceof Double) %7B%0A      return FloatData.forValue((Double) obj);%0A    %7D else if (obj instanceof Float) %7B%0A      // Automatically convert float to double.%0A      return FloatData.forValue((Float) obj);%0A
     
@@ -2684,16 +2684,177 @@
  else %7B%0A
+      SoyValue soyValue = SoyValueConverter.INSTANCE.convert(obj).resolve();%0A      if (soyValue instanceof SoyData) %7B%0A        return (SoyData) soyValue;%0A      %7D%0A
       th

Support all values supported by SoyValueConverter in SoyData.