/**
* Copyright 2010 CosmoCode GmbH
*
* 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 de.cosmocode.palava.ipc.xml.rpc.adapters;
import java.io.InputStream;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.SortedMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.ImmutableSortedMap.Builder;
import com.google.common.collect.Ordering;
import com.google.inject.Inject;
import com.google.inject.TypeLiteral;
import de.cosmocode.commons.Calendars;
import de.cosmocode.palava.ipc.xml.rpc.XmlRpc;
import de.cosmocode.palava.ipc.xml.rpc.generated.ObjectFactory;
import de.cosmocode.palava.ipc.xml.rpc.generated.Value;
/**
* A {@link Object} to {@link Object} adapter.
*
* @since 1.0
* @author Willi Schoenborn
*/
final class ObjectAdapter implements Adapter<Value, Object> {
static final TypeLiteral<Adapter<Value, Object>> LITERAL =
new TypeLiteral<Adapter<Value, Object>>() { };
private static final Logger LOG = LoggerFactory.getLogger(ObjectAdapter.class);
/**
* Orders classes by hierarchy, sub classes are considered less than super classes.
*/
private static final Ordering<Class<?>> HIERARCHY = new Ordering<Class<?>>() {
@Override
public int compare(Class<?> left, Class<?> right) {
if (left.equals(right)) {
return 0;
} else if (left.isAssignableFrom(right)) {
return 1;
} else if (right.isAssignableFrom(left)) {
return -1;
} else {
return left.getName().compareTo(right.getName());
}
}
};
private final Value nullValue;
private final SortedMap<Class<?>, Adapter<Value, ?>> adapters;
@Inject
public ObjectAdapter(
@XmlRpc ObjectFactory factory,
Adapter<Value, Boolean> booleanAdapter,
Adapter<Value, Date> dateAdapter,
Adapter<Value, Double> doubleAdapter,
Adapter<Value, InputStream> streamAdapter,
Adapter<Value, Integer> integerAdapter,
Adapter<Value, List<Object>> listAdapter,
Adapter<Value, Map<String, Object>> mapAdapter,
Adapter<Value, Number> numberAdapter,
Adapter<Value, String> stringAdapter) {
Preconditions.checkNotNull(factory, "Factory");
this.nullValue = factory.createValue();
this.nullValue.getContent().add(factory.createValueString("null"));
final Builder<Class<?>, Adapter<Value, ?>> builder = ImmutableSortedMap.orderedBy(HIERARCHY);
builder.put(Number.class, numberAdapter);
builder.put(Boolean.class, booleanAdapter);
builder.put(Calendar.class, Adapters.composeEncoder(dateAdapter, Calendars.getTime()));
builder.put(Date.class, dateAdapter);
builder.put(Double.class, doubleAdapter);
builder.put(InputStream.class, streamAdapter);
builder.put(Integer.class, integerAdapter);
builder.put(List.class, listAdapter);
builder.put(Map.class, mapAdapter);
builder.put(String.class, stringAdapter);
this.adapters = builder.build();
}
private Adapter<Value, ?> getDecoder(Value value) {
final ValueType type = ValueType.of(value);
final Class<?> javaType = type.getType();
final Adapter<Value, ?> adapter = adapters.get(javaType);
Preconditions.checkState(adapter != null, "No adapter configured for %s", javaType);
return adapter;
}
@SuppressWarnings("unchecked")
private Adapter<Value, Object> getEncoder(Class<?> type) {
for (Entry<Class<?>, Adapter<Value, ?>> entry : adapters.entrySet()) {
if (entry.getKey().isAssignableFrom(type)) {
return (Adapter<Value, Object>) entry.getValue();
}
}
return null;
}
@Override
public Object decode(Value input) {
Preconditions.checkNotNull(input, "Input");
return getDecoder(input).decode(input);
}
@Override
public Value encode(Object input) {
if (input == null) return nullValue;
final Adapter<Value, Object> adapter = getEncoder(input.getClass());
if (adapter == null) {
Preconditions.checkState(!(input instanceof String), "No adapter configured for Strings");
LOG.debug("No adapter configured for {}, using toString", input.getClass());
return encode(input.toString());
}
return adapter.encode(input);
}
}