/***
 * Copyright (c) 2009 Caelum - www.caelum.com.br/opensource All rights reserved.
 *
 * 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 br.com.caelum.vraptor.serialization.gson;

import static br.com.caelum.vraptor.proxy.CDIProxies.extractRawTypeIfPossible;
import static java.util.Collections.singletonList;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Any;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;

import com.google.gson.ExclusionStrategy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonSerializer;

import br.com.caelum.vraptor.core.ReflectionProvider;
import br.com.caelum.vraptor.serialization.Serializee;

/**
 * Builder Wrapper for JSON using GSON.
 * 
 * @author Rafael Dipold
 */
@Dependent
public class GsonBuilderWrapper implements GsonSerializerBuilder, GsonDeserializerBuilder {
	
	private final GsonBuilder builder = new GsonBuilder();
	private boolean withoutRoot;
	private String alias;
	private final List<ExclusionStrategy> exclusions;
	
	private final Serializee serializee;
	private final Iterable<JsonSerializer<?>> jsonSerializers;
	private final Iterable<JsonDeserializer<?>> jsonDeserializers;

	@Inject
	public GsonBuilderWrapper(@Any Instance<JsonSerializer<?>> jsonSerializers, 
			@Any Instance<JsonDeserializer<?>> jsonDeserializers,
			Serializee serializee, ReflectionProvider reflectionProvider) {
		this.jsonSerializers = jsonSerializers;
		this.jsonDeserializers = jsonDeserializers;
		this.serializee = serializee;
		ExclusionStrategy exclusion = new Exclusions(serializee, reflectionProvider);
		exclusions = singletonList(exclusion);
	}

	@Override
	public Gson create() {
		for (JsonSerializer<?> adapter : jsonSerializers) {
			registerAdapter(getAdapterType(adapter), adapter);
		}

		for (JsonDeserializer<?> adapter : jsonDeserializers) {
			registerAdapter(getAdapterType(adapter), adapter);
		}
		
		for (ExclusionStrategy exclusion : exclusions) {
			getGsonBuilder().addSerializationExclusionStrategy(exclusion);
		}
		
		return getGsonBuilder().create();
	}
	
	private void registerAdapter(Class<?> adapterType, Object adapter) {
		RegisterStrategy registerStrategy = adapter.getClass().getAnnotation(RegisterStrategy.class);
		if ((registerStrategy != null) && (registerStrategy.value().equals(RegisterType.SINGLE))) {
			getGsonBuilder().registerTypeAdapter(adapterType, adapter);
		} else {
			getGsonBuilder().registerTypeHierarchyAdapter(adapterType, adapter);
		}	
	}
	
	private Class<?> getAdapterType(Object adapter) {
		final Class<?> klazz = extractRawTypeIfPossible(adapter.getClass());
		final Type[] genericInterfaces = klazz.getGenericInterfaces();
		final ParameterizedType type = (ParameterizedType) genericInterfaces[0];
		final Type actualType = type.getActualTypeArguments()[0];

		if (actualType instanceof ParameterizedType) {
			return (Class<?>) ((ParameterizedType) actualType).getRawType();
		} else {
			return (Class<?>) actualType;
		}
	}

	@Override
	public Serializee getSerializee() {
		return serializee;
	}

	@Override
	public boolean isWithoutRoot() {
		return withoutRoot;
	}

	@Override
	public void setWithoutRoot(boolean withoutRoot) {
		this.withoutRoot = withoutRoot;		
	}

	@Override
	public String getAlias() {
		return alias;
	}

	@Override
	public void setAlias(String alias) {
		this.alias = alias;
	}

	@Override
	public void indented() {
		getGsonBuilder().setPrettyPrinting();
	}

	@Override
	public void setExclusionStrategies(ExclusionStrategy... strategies) {
		getGsonBuilder().setExclusionStrategies(strategies);
	}

	protected GsonBuilder getGsonBuilder() {
		return builder;
	}

	@Override
	public void version(double versionNumber) {
		getGsonBuilder().setVersion(versionNumber);
	}

	@Override
	public void serializeNulls() {
		getGsonBuilder().serializeNulls();
	}

}

@@ -2763,21 +2763,23 @@
 %09%7D%0A%09%0A%09pr
-ivate
+otected
  void re

Change visibility to protected for possible override.