package com.revolsys.io.map;
import java.io.File;
import java.lang.reflect.Constructor;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.BiConsumer;
import com.revolsys.collection.map.LinkedHashMapEx;
import com.revolsys.collection.map.MapEx;
import com.revolsys.collection.map.Maps;
import com.revolsys.logging.Logs;
import com.revolsys.properties.ObjectWithProperties;
import com.revolsys.record.io.format.json.Json;
import com.revolsys.spring.resource.PathResource;
import com.revolsys.spring.resource.Resource;
import com.revolsys.util.JavaBeanUtil;
import com.revolsys.util.Property;
public interface MapObjectFactory {
static String TYPE = "j:type";
static String TYPE_CLASS = "j:typeClass";
static String getType(final Map<String, ? extends Object> map) {
String type = Maps.getString(map, TYPE);
if (type == null) {
type = Maps.getString(map, "type");
}
return type;
}
static String getType(final ObjectWithProperties map) {
return getType(map.getProperties());
}
static String getTypeClass(final Map<String, ? extends Object> map) {
String type = Maps.getString(map, TYPE_CLASS);
if (type == null) {
type = Maps.getString(map, "typeClass");
}
return type;
}
@SuppressWarnings("unchecked")
static <V> V objectToObject(final Object value) {
try {
if (value instanceof Map) {
final Map<String, ? extends Object> valueMap = (Map<String, ? extends Object>)value;
return toObject(valueMap);
} else if (value instanceof List) {
final List<Object> newList = new ArrayList<>();
final List<?> values = (List<?>)value;
for (final Object listValue : values) {
final Object newValue = objectToObject(listValue);
newList.add(newValue);
}
return (V)newList;
}
} catch (final Throwable e) {
Logs.debug(MapObjectFactory.class, "Unable to convert:" + value, e);
}
return (V)value;
}
static void setType(final Map<String, ? super Object> map, final String type) {
if (Property.hasValue(type)) {
map.put(TYPE, type);
map.put("type", type);
}
}
@SuppressWarnings("unchecked")
static <V> V toObject(final Map<String, ? extends Object> map) {
if (map == null) {
return null;
} else {
final MapEx objectMap = new LinkedHashMapEx();
for (final Entry<String, ? extends Object> entry : map.entrySet()) {
final String key = entry.getKey();
Object value = entry.getValue();
value = objectToObject(value);
objectMap.put(key, value);
}
final String typeClass = getTypeClass(objectMap);
if (Property.hasValue(typeClass)) {
final Constructor<V> configConstructor = JavaBeanUtil.getConstructor(typeClass, Map.class);
final V object;
if (configConstructor == null) {
object = (V)JavaBeanUtil.createInstance(typeClass);
ObjectWithProperties.setProperties(object, objectMap);
} else {
object = JavaBeanUtil.invokeConstructor(configConstructor, objectMap);
}
return object;
} else {
final String type = getType(objectMap);
final MapObjectFactory objectFactory = MapObjectFactoryRegistry.getFactory(type);
if (objectFactory == null) {
return (V)objectMap;
} else {
return (V)objectFactory.mapToObject(objectMap);
}
}
}
}
static <V> V toObject(final Object source) {
final Resource resource = Resource.getResource(source);
final Resource oldResource = Resource.setBaseResource(resource.getParent());
try {
final MapEx properties = Json.toMap(resource);
return toObject(properties);
} catch (final Throwable t) {
Logs.error(MapObjectFactoryRegistry.class, "Cannot load object from " + resource, t);
return null;
} finally {
Resource.setBaseResource(oldResource);
}
}
/**
* Convert the resource specified in the source parameter to an object. The properties parameter
* will override those specific properties.
*
* @param source
* @param properties
* @return
*/
static <V> V toObject(final Object source, final Map<String, ? extends Object> properties) {
final Resource resource = Resource.getResource(source);
final Resource oldResource = Resource.setBaseResource(resource.getParent());
try {
final MapEx resourceProperties = Json.toMap(resource);
resourceProperties.putAll(properties);
return toObject(resourceProperties);
} catch (final Throwable t) {
Logs.error(MapObjectFactoryRegistry.class, "Cannot load object from " + resource, t);
return null;
} finally {
Resource.setBaseResource(oldResource);
}
}
/**
* Convert the resource specified in the source parameter to an object. The properties parameter
* will override those specific properties.
*
* @param source
* @param properties
* @return
*/
static <V> V toObject(final Object source, final Map<String, ? extends Object> properties,
final BiConsumer<Resource, Throwable> errorHandler) {
final Resource resource = Resource.getResource(source);
final Resource oldResource = Resource.setBaseResource(resource.getParent());
try {
final MapEx resourceProperties = Json.toMap(resource);
resourceProperties.putAll(properties);
return toObject(resourceProperties);
} catch (final Throwable t) {
errorHandler.accept(resource, t);
return null;
} finally {
Resource.setBaseResource(oldResource);
}
}
static String toString(final MapSerializer serializer) {
final Map<String, Object> properties = serializer.toMap();
return Json.toString(properties);
}
static void write(final File file, final MapSerializer serializer) {
final Map<String, Object> properties = serializer.toMap();
Json.writeMap(properties, file, true);
}
static void write(final Path path, final MapSerializer serializer) {
final PathResource resource = new PathResource(path);
write(resource, serializer);
}
static void write(final Resource resource, final MapSerializer serializer) {
final Map<String, Object> properties = serializer.toMap();
Json.writeMap(properties, resource, true);
}
String getDescription();
String getTypeName();
<V> V mapToObject(Map<String, ? extends Object> map);
}