package js.http.form;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.net.URL;
import java.util.Collection;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import js.converter.Converter;
import js.converter.ConverterException;
import js.converter.ConverterRegistry;
import js.http.ContentType;
import js.log.Log;
import js.log.LogFactory;
import js.util.Classes;
import js.util.Files;
import js.util.Params;
import js.util.Strings;
import js.util.Types;

/**
 * Map form fields to plain Java object of not restricted graph complexity. In order to locate related object fields uses a
 * proprietary syntax for field names. It is named Object Property Path and allows to locate any field from object
 * graph. It is an hierarchical path like name, using dots for path component separators. For example contact.name
 * refers to name field of contact object from this form object. When used with arrays and lists, field name is the index, e.g.
 * contacts.1.name is the name of the second contact item.
 *

 * Although FormObject class is public it is designed for framework internal usage; it helps injecting arbitrary Java objects on
 * resource or service methods. Container uses FormObject to create and initialize customer instance then inject
 * {@link #getValue()} as managed method argument. Note that form field names should obey Object Property Path
 * syntax and that managed method signature should have a single formal parameter.
 *
 *

 * @Override
 * public void saveCustomer(Customer customer) {
 * 	...
 * }
 *

 *

 * Current implementation has couple limitations regarding aggregated types: supports only arrays and lists, both objects and
 * primitives. Primitives values are converted to component type using {@link Converter}. Arrays should be initialized with the
 * right dimension. Ignore form fields if array length is smaller that form values count.
 *

 * This class implements best effort approach. If a property path does not denote an existing object field related form field is
 * ignored. Also, already mentioned, items exceeding array length are ignored.
 *
 * @author Iulian Rotaru
 * @version final
 */
public final class FormObject {
	/** Class logger. */
	private static final Log log = LogFactory.getLog(FormObject.class);

	/** Factory for object and graph node instances. */
	private final NodeFactory factory;

	/** Object instance that need to be populated from form parts. */
	private Object object;

	/**
	 * Test constructor.
	 *
	 * @param object object instance to populate.
	 */
	private FormObject(Object object) {
		this.factory = new NodeFactory();
		this.object = object;
	}

	/**
	 * Create form object instance of requested type and initialized it from HTTP request. HTTP request content type should be
	 * {@link ContentType#MULTIPART_FORM}. Field values are loaded in memory as object fields. If form contains streams they are
	 * saved on temporary files. Be aware that temporary files are removed at JVM exit.
	 *
	 * @param httpRequest HTTP request carrying the multipart form,
	 * @param type object instance type.
	 * @throws IOException if HTTP request input stream read operation fails.
	 */
	public FormObject(HttpServletRequest httpRequest, Class<?> type) throws IOException {
		this(Classes.newInstance(type));

		FormIterator formIterator = new FormIteratorImpl(httpRequest);
		formIterator.forEach(new FormHandler() {
			@Override
			protected void stream(String name, String fileName, String contentType, InputStream inputStream) throws Throwable {
				setValue(name, new UploadedFileImpl(name, fileName, contentType, Files.copy(inputStream)));
			}

			@Override
			protected void field(String name, String value) throws Throwable {
				setValue(name, value);
			}
		});
	}

	/**
	 * Set value to object field identified by object property path. Form this class perspective an object is a graph of value
	 * types. A value type is a primitive value or a related boxing class. Also any class that can be converted to a primitive
	 * are included; for example {@link File} or {@link URL} are value types since can be converted to/from strings. Opposite to
	 * value types are compound entities, that is, objects, arrays and collections that aggregates value types or other compound
	 * entities.
	 *

	 * To sum up, an object is a graph with compound entities and value types as nodes where value types are leafs. The
	 * propertyPath is the path through graph nodes till reach the value type and basically is a dot separated
	 * field names list.
	 *
	 * @param propertyPath object property path,
	 * @param value value to set.
	 * @throws ConverterException if value is primitive and fail to convert to field type.
	 * @throws IllegalAccessException this exception is required by reflective field signature but never thrown.
	 */
	private void setValue(String propertyPath, Object value) throws ConverterException, IllegalAccessException {
		List<String> nodeIDs = Strings.split(propertyPath, '.');
		int lastNodeIndex = nodeIDs.size() - 1;
		Node node = new ObjectNode(object);
		for (int index = 0; index < lastNodeIndex; index++) {
			node = node.getChild(nodeIDs.get(index));
			if (node == null) {
				return;
			}
		}
		node.setValue(nodeIDs.get(lastNodeIndex), value);
	}

	/**
	 * Get initialized form object with type provided to constructor.
	 *
	 * @return form object.
	 */
	public Object getValue() {
		return object;
	}

	// --------------------------------------------------------------------------------------------
	// INTERNAL CLASSES

	/**
	 * A node from object graph that can be a value type or a compound entity. A value type is a primitive or a class that can
	 * be converted to a primitive. A compound entity aggregates value types or other compound entities.
	 *

	 * A node has a value setter - if value type and a collection of children identified by an ID. A child ID can be a field
	 * name or an array/list index.
	 *
	 * @author Iulian Rotaru
	 * @version final
	 */
	private static interface Node {
		/**
		 * Set value to child node identified by given ID.
		 *
		 * @param childID child ID is a field name or array/list index.
		 * @param value node value.
		 * @throws ConverterException if value is primitive and fail to convert to field type.
		 * @throws IllegalAccessException this exception is required by reflective field signature but never thrown.
		 */
		void setValue(String childID, Object value) throws ConverterException, IllegalAccessException;

		/**
		 * Returns child node identified by its ID or null if not found. A child ID is the field name or array/list index.
		 *
		 * @param childID child ID is a field name or array/list index.
		 * @return child node or null.
		 * @throws IllegalAccessException this exception is required by reflective field signature but never thrown.
		 */
		Node getChild(String childID) throws IllegalAccessException;
	}

	/**
	 * Factory for object and graph node instances.
	 *
	 * @author Iulian Rotaru
	 * @version final
	 */
	private class NodeFactory {
		/**
		 * Create an object instance of the requested type. If type is a {@link List} uses
		 * {@link Classes#getListDefaultImplementation(Type)}.
		 *
		 * @param type object type.
		 * @return newly created instance.
		 */
		public Object newInstance(Class<?> type) {
			if (Types.isKindOf(type, List.class)) {
				type = Classes.getListDefaultImplementation(type);
			}
			return Classes.newInstance(type);
		}

		/**
		 * Create form object node wrapping given plain Java object.
		 *
		 * @param object plain Java object that created form object node should wrap,
		 * @param type component type used only if wrapped object is a list.
		 * @return node for requested class.
		 */
		public Node createNode(Object object, Class<?> type) {
			if (Types.isKindOf(object.getClass(), List.class)) {
				return new ListNode(object, type);
			}
			if (object.getClass().isArray()) {
				return new ArrayNode(object);
			}
			return new ObjectNode(object);
		}
	}

	/**
	 * Object graph node of generic object type.
	 *
	 * @author Iulian Rotaru
	 * @version final
	 */
	private class ObjectNode implements Node {
		/** Field string value converter to object field type. */
		private final Converter converter;

		/** Node wrapped object. */
		private final Object object;

		/**
		 * Construct node object.
		 *
		 * @param object wrapped object.
		 */
		public ObjectNode(Object object) {
			this.converter = ConverterRegistry.getConverter();
			this.object = object;
		}

		/**
		 * Get child node by name creating it on the fly if not set. This method attempt to retrieve child node for wrapped
		 * object field with requested name; it returns null if there is no object field with requested child name. If field
		 * value is null create child node and set it.
		 *
		 * @param childName child name.
		 * @return child node, newly created or retrieved from wrapped object field.
		 */
		@Override
		public Node getChild(String childName) throws IllegalAccessException {
			Field field = null;
			try {
				field = Classes.getFieldEx(object.getClass(), Strings.toMemberName(childName));
			} catch (NoSuchFieldException e) {
				// best effort approach: form to object mapping is not so strictly but still log missing fields
				log.debug("Missing field |%s| from object |%s|.", childName, object.getClass());
				return null;
			}

			Object child = field.get(object);
			// not test coverable condition: if child is null child instanceof Collection cannot be true
			if (child == null || (child instanceof Collection<?> && ((Collection<?>) child).size() == 0)) {
				child = FormObject.this.factory.newInstance(field.getType());
				field.set(object, child);
			}
			return FormObject.this.factory.createNode(child, type(field));
		}

		/**
		 * Set value to wrapped object field identified by requested child name. Given string value is converted to wrapped
		 * object type using {@link Converter}.
		 *
		 * @param childName wrapped object field name,
		 * @param value string value loaded from form field, null and empty accepted.
		 * @throws ConverterException if field value conversion fails.
		 * @throws IllegalAccessException this exception is required by reflective field signature but never thrown.
		 */
		@Override
		public void setValue(String childName, Object value) throws ConverterException, IllegalAccessException {
			Field field = null;
			try {
				field = Classes.getFieldEx(object.getClass(), Strings.toMemberName(childName));
			} catch (NoSuchFieldException e) {
				// best effort approach: form to object mapping is not so strictly - i.e. ignores missing fields
				log.debug("Missing field |%s| from object |%s|.", childName, object.getClass());
				return;
			}

			if (value instanceof UploadedFile) {
				if (Types.isKindOf(field.getType(), File.class)) {
					field.set(object, ((UploadedFile) value).getFile());
				} else {
					field.set(object, value);
				}
			} else {
				field.set(object, converter.asObject((String) value, field.getType()));
			}
		}
	}

	/**
	 * Object graph node of list type.
	 *
	 * @author Iulian Rotaru
	 * @version final
	 */
	@SuppressWarnings("unchecked")
	private class ListNode implements Node {
		/** Field string value converter to list component type. */
		private final Converter converter;

		/** Node wrapped list. */
		private final List<Object> list;

		/** List component type. */
		private final Class<?> componentType;

		/**
		 * Construct list node for a given list component type.
		 *
		 * @param object wrapped list,
		 * @param componentType list component type.
		 */
		public ListNode(Object object, Class<?> componentType) {
			this.converter = ConverterRegistry.getConverter();
			this.list = (List<Object>) object;
			this.componentType = componentType;
		}

		/**
		 * Get child node at specified index. Child node type depends on {@link #componentType}.
		 *
		 * @param childIndex child index.
		 * @return child node at requested index.
		 * @throws IllegalArgumentException if child index argument is not a numeric value.
		 */
		@Override
		public Node getChild(String childIndex) throws IllegalArgumentException {
			Params.isNumeric(childIndex, "Child index");
			int index = Integer.parseInt(childIndex);
			ensureCapacity(index, componentType);
			return FormObject.this.factory.createNode(list.get(index), componentType);
		}

		/**
		 * Set value to child node identified by given child index. Given string value is converted to list component type, see
		 * {@link #componentType} using {@link Converter}.
		 *
		 * @param childIndex child index,
		 * @param value string value, null and empty accepted, loaded from field.
		 * @throws IllegalArgumentException if child index argument is not a numeric value.
		 * @throws ConverterException if field value conversion fails.
		 */
		@Override
		public void setValue(String childIndex, Object value) throws IllegalArgumentException, ConverterException {
			Params.isNumeric(childIndex, "Child index");
			int index = Integer.parseInt(childIndex);
			ensureCapacity(index);
			if (!String.class.equals(componentType)) {
				value = converter.asObject((String) value, componentType);
			}
			list.set(index, value);
		}

		/**
		 * Add null value(s) to wrapped list till given index, ensuring list capacity.
		 *
		 * @param index list index till where to add null value(s).
		 */
		private void ensureCapacity(int index) {
			int overflow = index - list.size();
			while (overflow-- >= 0) {
				list.add(null);
			}
		}

		/**
		 * Add item(s) of requested type to wrapped list till index position, ensuring list capacity.
		 *
		 * @param index list index till where item(s) should be added,
		 * @param type the type of item instance to create.
		 */
		private void ensureCapacity(int index, Class<?> type) {
			int overflow = index - list.size();
			while (overflow-- >= 0) {
				list.add(FormObject.this.factory.newInstance(type));
			}
		}
	}

	/**
	 * Object graph node of array type. This node is specialized on array fields. Note that array field should be initialized
	 * with dimension known on form development. If want to avoid dependency on form design uses list type, see {@link ListNode}
	 * .
	 *
	 * @author Iulian Rotaru
	 * @version final
	 */
	private class ArrayNode implements Node {
		/** Field string value converter to array component type. */
		private final Converter converter;

		/** Node wrapped array. */
		private final Object array;

		/** Array component type. */
		private final Class<?> componentType;

		/**
		 * Construct array node.
		 *
		 * @param array wrapped array.
		 */
		public ArrayNode(Object array) {
			this.converter = ConverterRegistry.getConverter();
			this.array = array;
			this.componentType = array.getClass().getComponentType();
		}

		/**
		 * Get child node at specified index, creating it on the fly if missing. Child node type depends on
		 * {@link #componentType}.
		 *
		 * @param childIndex child index.
		 * @return child node at requested index, freshly created or reused from array.
		 * @throws IllegalArgumentException if child index argument is not a numeric value.
		 */
		@Override
		public Node getChild(String childIndex) throws IllegalArgumentException {
			Params.isNumeric(childIndex, "Child index");
			int index = Integer.parseInt(childIndex);
			Object item = Array.get(array, index);
			if (item == null) {
				Array.set(array, index, item = FormObject.this.factory.newInstance(componentType));
			}
			return FormObject.this.factory.createNode(item, componentType);
		}

		/**
		 * Set value to child specified by given index. If child index exceeds array length this setter does nothing and value
		 * is ignored.
		 *

		 * Given string value is converted to array type, see {@link #componentType} using {@link Converter}.
		 *
		 * @param childIndex node child index,
		 * @param value string value, null and empty accepted, loaded from field.
		 * @throws IllegalArgumentException if child index argument is not a numeric value.
		 * @throws ConverterException if field value conversion fails.
		 */
		@Override
		public void setValue(String childIndex, Object value) throws IllegalArgumentException, ConverterException {
			Params.isNumeric(childIndex, "Child index");
			int index = Integer.parseInt(childIndex);
			if (index >= Array.getLength(array)) {
				log.debug("Array capacity exceeded. Ingore value |%s| for array index |%d|.", value, index);
				return;
			}
			if (!String.class.equals(componentType)) {
				value = converter.asObject((String) value, componentType);
			}
			Array.set(array, index, value);
		}
	}

	/**
	 * Return field class or actual type argument if given field is a list.
	 *
	 * @param field Java reflective class field.
	 * @return field type.
	 */
	private static Class<?> type(Field field) {
		if (Types.isKindOf(field.getType(), List.class)) {
			// for the purpose of this implementation only first parameterized
			// type matters, as result from list declaration List<E>
			return (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
		}
		return field.getType();
	}
}


package js.container;

import java.lang.reflect.Proxy;
import java.util.Stack;

import js.core.AppFactory;
import js.lang.BugError;
import js.log.Log;
import js.log.LogFactory;
import js.util.Classes;
import js.util.Types;

/**
 * Base class for all processors dealing with dependencies. Supplies utility method for dependency instance retrieval with guard
 * against circular dependencies. Usually {@link #getDependencyValue(ManagedClassSPI, Class)} expect as dependency type a
 * managed class and just delegate container for instance retrieval, see
 * {@link AppFactory#getOptionalInstance(Class, Object...)}. Anyway, if dependency type is not a managed class tries to
 * instantiate it with standard {@link Class#newInstance()}; of course type should be concrete class and have default
 * constructor. Otherwise throws bug error.
 *

 * Depending on host and dependency managed classes scope is possible that dependency value to be replaced by a scope proxy, see
 * {@link ScopeProxyHandler}. This is to adapt dependency with shorted life span into host with larger life span; otherwise
 * dependency may become invalid while host instance is still active. Logic to detect if scope proxy is required is encapsulated
 * into separated utility method, see {@link #isProxyRequired(ManagedClassSPI, ManagedClassSPI)}.
 *
 *
Circular Dependencies

 *

 * When container creates a new managed instance there will be a call to this class utility method. On its turn this class
 * utility method may use container to retrieve instance for a dependency; this creates recursive calls that allows for
 * unrestricted graph but is prone to circular dependencies.
 *

 * This class keeps a stack trace for classes in dependencies chain and throws bug error if discover a request for a dependency
 * type that is already into stack trace. Since stack trace is kept on thread local storage circular dependencies guard works
 * only in current thread.
 *
 *
App Factory

 *

 * Application factory and its hierarchy cannot be managed classes since their job is to manage managed classes. If requested
 * dependency type is a kind of application factory, container cannot be used for instance retrieval. This is special
 * case handled by {@link #getDependencyValue(ManagedClassSPI, Class)} with special logic: if dependency type is a kind of
 * {@link AppFactory} returns container reference provided by host managed class, see {@link ManagedClassSPI#getContainer()}.
 *
 * @author Iulian Rotaru
 * @version final
 */
abstract class DependencyProcessor {
	/** Class logger. */
	private static final Log log = LogFactory.getLog(DependencyProcessor.class);

	/** Thread local storage for dependencies trace stack. Used to prevent circular dependencies. */
	private static ThreadLocal<Stack<Class<?>>> dependenciesStack = new ThreadLocal<>();

	/**
	 * Get dependency value of requested type. See class description for a discussion about supported types and circular
	 * dependencies.
	 *
	 * @param hostManagedClass managed class defining the context on which dependency is resolved,
	 * @param type dependency type.
	 * @return dependency instance, reused from container caches or fresh created.
	 * @throws BugError if dependency value cannot be created or circular dependency is detected.
	 */
	@SuppressWarnings("unchecked")
	protected static Object getDependencyValue(ManagedClassSPI hostManagedClass, Class<?> type) {
		Stack<Class<?>> stackTrace = dependenciesStack.get();
		if (stackTrace == null) {
			stackTrace = new Stack<>();
			dependenciesStack.set(stackTrace);
		}
		ContainerSPI container = hostManagedClass.getContainer();

		if (stackTrace.contains(type)) {
			try {
				// add current dependency class to reveal what dependency from stack is circular
				stackTrace.add(type);

				StringBuilder builder = new StringBuilder();
				builder.append("Circular dependency. Dependencies trace follows:\r\n");
				for (Class<?> stackTraceClass : stackTrace) {
					builder.append("\t- ");
					builder.append(stackTraceClass.getName());
					builder.append("\r\n");
				}

				log.error(builder.toString());
				throw new BugError("Circular dependency for |%s|.", type.getName());
			} finally {
				// takes care to current thread stack trace is removed
				dependenciesStack.remove();
			}
		}

		stackTrace.push(type);
		try {

			ManagedClassSPI dependencyManagedClass = container.getManagedClass(type);
			if (isProxyRequired(hostManagedClass, dependencyManagedClass)) {
				// if scope proxy is required returns a Java Proxy handled by ScopeProxyHandler
				ScopeProxyHandler<?> handler = new ScopeProxyHandler<>(container, type);
				return Proxy.newProxyInstance(dependencyManagedClass.getImplementationClass().getClassLoader(), dependencyManagedClass.getInterfaceClasses(), handler);
			}

			Object value = container.getOptionalInstance((Class<? super Object>) type);
			if (value != null) {
				// if dependency type is a managed class returns it value from factory
				return value;
			}

			if (Types.isKindOf(type, AppFactory.class)) {
				// handle ApFactory and its hierarchy since it is a special case
				return container;
			}

			if (Classes.isInstantiable(type)) {
				// if requested type is instantiable POJO create a new empty instance of requested type
				return Classes.newInstance(type);
			}

			// TODO: test value instance of
			// if FactoryBean consider it as factory and substitute value
			// e.g. value = ((FactoryBean)value).getInstance(value.getClass())

			// all attempts to create dependency value has fallen
			throw new BugError("Dependency |%s| not resolved for |%s|.", type.getName(), hostManagedClass);

		} finally {
			stackTrace.pop();
			// do not remove stack trace after outermost call finished, i.e. when stack trace is empty
			// leave it on thread local for reuse, in order to avoid unnecessary object creation
		}
	}

	/**
	 * Compare host and dependency managed classes scope and decide if scope proxy is required. Current implementation enable
	 * scope proxy only when dependency has {@link InstanceScope#SESSION} scope.
	 *
	 * @param hostManagedClass managed class defining the context on which dependency is resolved,
	 * @param dependencyManagedClass dependency managed class, possible null.
	 * @return true if scope proxy is required.
	 */
	private static boolean isProxyRequired(ManagedClassSPI hostManagedClass, ManagedClassSPI dependencyManagedClass) {
		if (dependencyManagedClass != null) {
			InstanceScope dependencyScope = dependencyManagedClass.getInstanceScope();
			if (InstanceScope.THREAD.equals(dependencyScope)) {
				return InstanceScope.APPLICATION.equals(hostManagedClass.getInstanceScope());
			}
			return InstanceScope.SESSION.equals(dependencyScope);
		}
		return false;
	}
}


/**
 * Copyright (C) 2015 Michael Schnell. All rights reserved.
 * http://www.fuin.org/
 *
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 3 of the License, or (at your option) any
 * later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library. If not, see http://www.gnu.org/licenses/.
 */
package org.fuin.srcgen4j.core.base;

import java.io.File;
import java.io.IOException;

import org.apache.commons.io.FileUtils;
import org.fuin.utils4j.Utils4J;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Generated file.
 */
public final class GeneratedFile {

    private static final Logger LOG = LoggerFactory.getLogger(GeneratedFile.class);

    private final File file;

    private final File tmpFile;

    private final String logInfo;

    private final boolean skip;

    private boolean persisted = false;

    /**
     * Constructor with file that will NOT be skipped.
     *
     * @param file
     *            Target file to generate to - Cannot be NULL.
     * @param logInfo
     *            Addition information to log.
     */
    public GeneratedFile(final File file, final String logInfo) {
        this(file, logInfo, false);
    }

    /**
     * Constructor with skip information.
     *
     * @param file
     *            Target file to generate to - Cannot be NULL.
     * @param logInfo
     *            Addition information to log.
     * @param skip
     *            If the file should NOT be generated TRUE, else FALSE.
     */
    public GeneratedFile(final File file, final String logInfo, final boolean skip) {
        super();
        this.file = file;
        this.tmpFile = new File(file + ".tmp");
        if (logInfo == null) {
            this.logInfo = "";
        } else {
            this.logInfo = " [" + logInfo + "]";
        }
        this.skip = skip;
    }

    /**
     * Returns the file to generate to. This is a reference to a temporary file. Calling {@link #persist()} will copy the temporary file to
     * the target file if the content of the file is not equal.
     *
     * @return Target file to generate to - Never NULL.
     */
    public final File getTmpFile() {
        return tmpFile;
    }

    /**
     * Returns the information if the file should be skipped.
     *
     * @return If the file should NOT be generated TRUE, else FALSE.
     */
    public final boolean isSkip() {
        return skip;
    }

    /**
     * Compares the content of the temporary file with the possibly existing target file. If both are equal the temporary file is deleted.
     * Otherwise the old target file is deleted and the new generated file is renamed. This prevents time stamp changes for the target file
     * if nothing changed since the last generation.
     */
    public final void persist() {

        if (persisted) {
            // Do nothing if already done
            return;
        }

        try {

            // Compare new and old file
            if (FileUtils.contentEquals(tmpFile, file)) {
                LOG.debug("Omitted: {} {}", getPath(), logInfo);
                if (!tmpFile.delete()) {
                    tmpFile.deleteOnExit();
                }
            } else {
                if (file.exists() && !file.delete()) {
                    throw new IOException("Wasn't able to delete file " + file);
                }
                if (!tmpFile.renameTo(file)) {
                    throw new IOException("Wasn't able to rename temporary file " + tmpFile + " to " + file);
                }
                LOG.info("Generated: {} {}", getPath(), logInfo);
            }

            persisted = true;

        } catch (final IOException ex) {
            throw new RuntimeException("Error comparing content: tmp=" + tmpFile + ", target=" + file + logInfo, ex);
        }

    }

    private String getPath() {
        return Utils4J.getCanonicalPath(file);
    }

    @Override
    public final String toString() {
        return getPath();
    }

}


package net.typedrest;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;
import java.util.function.Predicate;
import lombok.*;
import org.apache.http.Header;
import static org.apache.http.HttpHeaders.RETRY_AFTER;
import org.apache.http.HttpResponse;
import org.apache.http.client.fluent.Request;
import rx.Scheduler;
import rx.Subscription;
import rx.schedulers.Schedulers;
import rx.util.async.StoppableObservable;
import static java.lang.Thread.sleep;
import static rx.util.async.Async.runAsync;

/**
 * REST endpoint that represents an entity that can be polled for state changes.
 *
 * @param <TEntity> The type of entity the endpoint represents.
 */
public class PollingEndpointImpl<TEntity>
        extends ElementEndpointImpl<TEntity>
        implements PollingEndpoint<TEntity> {

    private final Predicate<TEntity> endCondition;

    /**
     * Creates a new polling endpoint.
     *
     * @param referrer The endpoint used to navigate to this one.
     * @param relativeUri The URI of this endpoint relative to the
     * referrer's.
     * @param entityType The type of entity the endpoint represents.
     */
    public PollingEndpointImpl(Endpoint referrer, URI relativeUri, Class<TEntity> entityType) {
        super(referrer, relativeUri, entityType);
        endCondition = null;
    }

    /**
     * Creates a new polling endpoint.
     *
     * @param referrer The endpoint used to navigate to this one.
     * @param relativeUri The URI of this endpoint relative to the
     * referrer's.
     * @param entityType The type of entity the endpoint represents.
     * @param endCondition A check to determine whether the entity has reached
     * its final state an no further polling is required.
     */
    public PollingEndpointImpl(Endpoint referrer, URI relativeUri, Class<TEntity> entityType, Predicate<TEntity> endCondition) {
        super(referrer, relativeUri, entityType);
        this.endCondition = endCondition;
    }

    /**
     * Creates a new polling endpoint.
     *
     * @param referrer The endpoint used to navigate to this one.
     * @param relativeUri The URI of this endpoint relative to the
     * referrer's. Prefix ./ to append a trailing
     * slash to the referrer URI if missing.
     * @param entityType The type of entity the endpoint represents.
     */
    public PollingEndpointImpl(Endpoint referrer, String relativeUri, Class<TEntity> entityType) {
        super(referrer, relativeUri, entityType);
        endCondition = null;
    }

    /**
     * Creates a new polling endpoint.
     *
     * @param referrer The endpoint used to navigate to this one.
     * @param relativeUri The URI of this endpoint relative to the
     * referrer's. Prefix ./ to append a trailing
     * slash to the referrer URI if missing.
     * @param entityType The type of entity the endpoint represents.
     * @param endCondition A check to determine whether the entity has reached
     * its final state an no further polling is required.
     */
    public PollingEndpointImpl(Endpoint referrer, String relativeUri, Class<TEntity> entityType, Predicate<TEntity> endCondition) {
        super(referrer, relativeUri, entityType);
        this.endCondition = endCondition;
    }

    @Override
    public StoppableObservable<TEntity> getObservable() {
        return getObservable(Schedulers.newThread());
    }

    @Override
    protected void handleResponse(HttpResponse response, Request request)
            throws IOException, IllegalArgumentException, IllegalAccessException, FileNotFoundException, IllegalStateException {
        Header retryAfterHeader = response.getFirstHeader(RETRY_AFTER);
        if (retryAfterHeader != null) {
            try {
                pollingInterval = Integer.parseInt(retryAfterHeader.getValue());
            } catch (NumberFormatException ex) {
            }
        }
        super.handleResponse(response, request);
    }

    @Getter
    @Setter
    private int pollingInterval = 3;

    /**
     * Provides an observable stream of element states. Compares entities using
     * {@link Object#equals(java.lang.Object)} to detect changes.
     *
     * @param pollingInterval The interval in milliseconds in which to send
     * requests to the server.
     * @param endCondition An optional predicate determining which entity state
     * ends the polling process.
     * @param scheduler The scheduler used to run the background thread.
     * @return An observable stream of element states.
     */
    StoppableObservable<TEntity> getObservable(Scheduler scheduler) {
        return runAsync(scheduler, (rx.Observer<? super TEntity> observer, Subscription subscription) -> {
            TEntity previousEntity;
            try {
                previousEntity = read();
            } catch (IOException | IllegalArgumentException | IllegalAccessException ex) {
                observer.onError(ex);
                return;
            }
            observer.onNext(previousEntity);

            while (endCondition == null || !endCondition.test(previousEntity)) {
                try {
                    sleep(pollingInterval * 1000);
                } catch (InterruptedException ex) {
                }
                if (subscription.isUnsubscribed()) {
                    break;
                }

                TEntity newEntity;
                try {
                    newEntity = read();
                } catch (IOException | IllegalArgumentException | IllegalAccessException ex) {
                    observer.onError(ex);
                    return;
                }
                if (!newEntity.equals(previousEntity)) {
                    observer.onNext(newEntity);
                }

                previousEntity = newEntity;
            }
            observer.onCompleted();
        });
    }
}


package net.typedrest;

import com.damnhandy.uri.template.UriTemplate;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.node.*;
import static net.typedrest.HeaderUtils.getLinkHeaders;
import static net.typedrest.URIUtils.ensureTrailingSlash;
import java.io.*;
import java.net.*;
import java.util.*;
import static java.util.Collections.*;
import static java.util.stream.Collectors.*;
import lombok.*;
import org.apache.http.*;
import org.apache.http.client.fluent.*;
import org.apache.http.util.*;
import static java.util.Arrays.stream;
import static java.util.Arrays.stream;
import static java.util.Arrays.stream;
import static java.util.Arrays.stream;

/**
 * Base class for building REST endpoints, i.e. remote HTTP resources.
 */
public abstract class AbstractEndpoint
        implements Endpoint {

    @Getter
    protected final URI uri;

    @Getter
    protected final Executor executor;

    @Getter
    protected final ObjectMapper serializer;

    /**
     * A set of default HTTP headers to be added to each request.
     */
    protected final Collection defaultHeaders = new LinkedList<>();

    /**
     * Creates a new REST endpoint with an absolute URI.
     *
     * @param uri The HTTP URI of the remote element.
     * @param executor The REST executor used to communicate with the remote
     * element.
     * @param serializer Controls the serialization of entities sent to and
     * received from the server.
     */
    protected AbstractEndpoint(URI uri, Executor executor, ObjectMapper serializer) {
        if (uri == null) {
            throw new IllegalArgumentException("uri must not be null.");
        }
        if (executor == null) {
            throw new IllegalArgumentException("executor must not be null.");
        }
        if (serializer == null) {
            throw new IllegalArgumentException("serializer must not be null.");
        }

        this.uri = uri;
        this.executor = executor;
        this.serializer = serializer;
    }

    /**
     * Creates a new REST endpoint with a relative URI.
     *
     * @param referrer The parent endpoint containing this one.
     * @param relativeUri The URI of this endpoint relative to the
     * referrer's.
     */
    protected AbstractEndpoint(Endpoint referrer, URI relativeUri) {
        this(referrer.getUri().resolve(relativeUri), referrer.getExecutor(), referrer.getSerializer());

        if (referrer instanceof AbstractEndpoint) {
            defaultHeaders.addAll(((AbstractEndpoint) referrer).defaultHeaders);
        }
    }

    /**
     * Creates a new REST endpoint with a relative URI.
     *
     * @param referrer The parent endpoint containing this one.
     * @param relativeUri The URI of this endpoint relative to the
     * referrer's. Prefix ./ to append a trailing
     * slash to the referrer URI if missing. Prefix ./
     * to append a trailing slash to the referrer URI if missing.
     */
    protected AbstractEndpoint(Endpoint referrer, String relativeUri) {
        this((relativeUri.startsWith("./") ? ensureTrailingSlash(referrer.getUri()) : referrer.getUri())
                .resolve(relativeUri), referrer.getExecutor(), referrer.getSerializer());

        if (referrer instanceof AbstractEndpoint) {
            defaultHeaders.addAll(((AbstractEndpoint) referrer).defaultHeaders);
        }
    }

    /**
     * Registers one or more default links for a specific relation type. These
     * links are used when no links with this relation type are provided by the
     * server.
     *
     * This method is not thread-safe! Call this before performing any requests.
     *
     * @param rel The relation type of the links to add.
     * @param hrefs The hrefs of links relative to this endpoint's URI. Use
     * null or an empty list to remove all previous entries for the
     * relation type.
     *
     * @see Endpoint#getLinks(java.lang.String)
     * @see Endpoint#getLinksWithTitles(java.lang.String)
     * @see Endpoint#link(java.lang.String)
     */
    public final void setDefaultLink(String rel, String... hrefs) {
        if (hrefs == null || hrefs.length == 0) {
            defaultLinks.remove(rel);
        } else {
            defaultLinks.put(rel,
                    stream(hrefs).map(uri::resolve).collect(toSet()));
        }
    }

    /**
     * Registers a default link template for a specific relation type. This
     * template is used when no template with this relation type is provided by
     * the server.
     *
     * This method is not thread-safe! Call this before performing any requests.
     *
     * @param rel The relation type of the link template to add.
     * @param href The href of the link template relative to this endpoint's
     * URI. Use null to remove any previous entry for the relation
     * type.
     *
     * @see Endpoint#linkTemplate(java.lang.String)
     */
    public final void setDefaultLinkTemplate(String rel, String href) {
        if (href == null) {
            defaultLinkTemplates.remove(rel);
        } else {
            defaultLinkTemplates.put(rel, href);
        }
    }

    /**
     * Executes a REST request and wraps HTTP status codes in appropriate
     * {@link Exception} types.
     *
     * @param request The request to execute.
     * @return The HTTP response to the request.
     *
     * @throws IOException Network communication failed.
     * @throws IllegalArgumentException {@link HttpStatus#SC_BAD_REQUEST}
     * @throws IllegalAccessException {@link HttpStatus#SC_UNAUTHORIZED} or
     * {@link HttpStatus#SC_FORBIDDEN}
     * @throws FileNotFoundException {@link HttpStatus#SC_NOT_FOUND} or
     * {@link HttpStatus#SC_GONE}
     * @throws IllegalStateException
     * {@link HttpStatus#SC_CONFLICT}, {@link HttpStatus#SC_PRECONDITION_FAILED}
     * or {@link HttpStatus#SC_REQUESTED_RANGE_NOT_SATISFIABLE}
     * @throws RuntimeException Other non-success status code.
     */
    protected HttpResponse executeAndHandle(Request request)
            throws IOException, IllegalArgumentException, IllegalAccessException, FileNotFoundException, IllegalStateException {
        HttpResponse response = execute(request);
        handleResponse(response, request);
        return response;
    }

    /**
     * Executes a REST request adding any configured {@link #defaultHeaders}.
     *
     * @param request The request to execute.
     * @return The HTTP response to the request.
     *
     * @throws IOException Network communication failed.
     * @throws RuntimeException Other non-success status code.
     */
    protected HttpResponse execute(Request request)
            throws IOException {
        defaultHeaders.forEach(request::addHeader);
        return executor.execute(request).returnResponse();
    }

    /**
     * Handles the response of a REST request and wraps HTTP status codes in
     * appropriate {@link Exception} types.
     *
     * @param response The response to handle.
     * @param request The original request the reponse is for.
     *
     * @throws IOException Network communication failed.
     * @throws IllegalArgumentException {@link HttpStatus#SC_BAD_REQUEST}
     * @throws IllegalAccessException {@link HttpStatus#SC_UNAUTHORIZED} or
     * {@link HttpStatus#SC_FORBIDDEN}
     * @throws FileNotFoundException {@link HttpStatus#SC_NOT_FOUND} or
     * {@link HttpStatus#SC_GONE}
     * @throws IllegalStateException
     * {@link HttpStatus#SC_CONFLICT}, {@link HttpStatus#SC_PRECONDITION_FAILED}
     * or {@link HttpStatus#SC_REQUESTED_RANGE_NOT_SATISFIABLE}
     * @throws RuntimeException Other non-success status code.
     */
    protected void handleResponse(HttpResponse response, Request request)
            throws IOException, IllegalArgumentException, IllegalAccessException, FileNotFoundException, IllegalStateException {
        handleLinks(response);
        handleCapabilities(response);
        handleErrors(response, request);
    }

    /**
     * Wraps HTTP status codes in appropriate {@link Exception} types.
     *
     * @param response The response to check for errors.
     * @param request The original request the reponse is for.
     *
     * @throws IOException Network communication failed.
     * @throws IllegalArgumentException {@link HttpStatus#SC_BAD_REQUEST}
     * @throws IllegalAccessException {@link HttpStatus#SC_UNAUTHORIZED} or
     * {@link HttpStatus#SC_FORBIDDEN}
     * @throws FileNotFoundException {@link HttpStatus#SC_NOT_FOUND} or
     * {@link HttpStatus#SC_GONE}
     * @throws IllegalStateException
     * {@link HttpStatus#SC_CONFLICT}, {@link HttpStatus#SC_PRECONDITION_FAILED}
     * or {@link HttpStatus#SC_REQUESTED_RANGE_NOT_SATISFIABLE}
     * @throws RuntimeException Other non-success status code.
     */
    protected void handleErrors(HttpResponse response, Request request)
            throws IOException, IllegalArgumentException, IllegalAccessException, FileNotFoundException, IllegalStateException {
        StatusLine statusLine = response.getStatusLine();
        if (statusLine.getStatusCode() <= 299) {
            return;
        }

        String message = request + " responded with " + statusLine.getStatusCode() + " " + statusLine.getReasonPhrase();

        HttpEntity entity = response.getEntity();
        String body;
        if (entity == null) {
            body = null;
        } else {
            body = EntityUtils.toString(entity);
            Header contentType = entity.getContentType();
            if ((contentType != null) && contentType.getValue().startsWith("application/json")) {
                try {
                    JsonNode messageNode = serializer.readTree(body).get("message");
                    if (messageNode != null) {
                        message = messageNode.asText();
                    }
                } catch (JsonProcessingException ex) {
                }
            }
        }

        Exception inner = (body == null) ? null : new HttpException(body);
        switch (statusLine.getStatusCode()) {
            case HttpStatus.SC_BAD_REQUEST:
                throw new IllegalArgumentException(message, inner);
            case HttpStatus.SC_UNAUTHORIZED:
                //throw new InvalidCredentialException(message);
                throw new IllegalAccessException(message);
            case HttpStatus.SC_FORBIDDEN:
                throw new IllegalAccessException(message);
            case HttpStatus.SC_NOT_FOUND:
            case HttpStatus.SC_GONE:
                throw new FileNotFoundException(message);
            case HttpStatus.SC_CONFLICT:
                throw new IllegalStateException(message, inner);
            case HttpStatus.SC_PRECONDITION_FAILED:
                //throw new VersionNotFoundException(message, inner);
                throw new IllegalStateException(message, inner);
            case HttpStatus.SC_REQUESTED_RANGE_NOT_SATISFIABLE:
                //throw new IndexOutOfBoundsException(message);
                throw new IllegalStateException(message, inner);
            case HttpStatus.SC_REQUEST_TIMEOUT:
            //throw new TimeoutException(message);
            default:
                throw new RuntimeException(message, inner);
        }
    }

    /**
     * Handles links embedded in an HTTP response.
     *
     * @param response The response to check for links.
     */
    @SuppressWarnings("LocalVariableHidesMemberVariable")
    private void handleLinks(HttpResponse response) {
        Map<String, Map<URI, String>> links = new HashMap<>();
        Map<String, String> linkTemplates = new HashMap<>();

        handleHeaderLinks(response, links, linkTemplates);

        HttpEntity entity = response.getEntity();
        if (entity != null) {
            Header contentType = entity.getContentType();
            if ((contentType != null) && contentType.getValue().startsWith("application/json")) {
                try {
                    handleBodyLinks(serializer.readTree(entity.getContent()), links, linkTemplates);
                } catch (IOException ex) {
                    throw new RuntimeException();
                    // Body error handling is done elsewhere
                }
            }
        }

        this.links = unmodifiableMap(links);
        this.linkTemplates = unmodifiableMap(linkTemplates);
    }

    /**
     * Handles links embedded in HTTP response headers.
     *
     * @param response The response to check for links.
     * @param links A dictionary to add found links to.
     * @param linkTemplates A dictionary to add found link templates to.
     */
    protected void handleHeaderLinks(HttpResponse response, Map<String, Map<URI, String>> links, Map<String, String> linkTemplates) {
        getLinkHeaders(response).forEach(header -> {
            if (header.getRel() == null) {
                return;
            }
            if (header.isTemplated()) {
                linkTemplates.put(header.getRel(), header.getHref());
            } else {
                getOrAdd(links, header.getRel())
                        .put(uri.resolve(header.getHref()), header.getTitle());
            }
        });
    }

    /**
     * Handles links embedded in JSON response bodies.
     *
     * @param jsonBody The body to check for links.
     * @param links A dictionary to add found links to.
     * @param linkTemplates A dictionary to add found link templates to.
     */
    protected void handleBodyLinks(JsonNode jsonBody, Map<String, Map<URI, String>> links, Map<String, String> linkTemplates) {
        if (jsonBody.getNodeType() != JsonNodeType.OBJECT) {
            return;
        }

        JsonNode linksNode = jsonBody.get("_links");
        if (linksNode == null) {
            linksNode = jsonBody.get("links");
        }
        if (linksNode == null) {
            return;
        }

        linksNode.fields().forEachRemaining(x -> {
            String rel = x.getKey();
            Map<URI, String> linksForRel = getOrAdd(links, rel);

            switch (x.getValue().getNodeType()) {
                case ARRAY:
                    x.getValue().forEach(subobj -> {
                        if (subobj.getNodeType() == JsonNodeType.OBJECT) {
                            parseLinkObject(rel, (ObjectNode) subobj, linksForRel, linkTemplates);
                        }
                    });
                    break;
                case OBJECT:
                    parseLinkObject(rel, (ObjectNode) x.getValue(), linksForRel, linkTemplates);
                    break;
            }
        });
    }

    /**
     * Parses a JSON object for link information.
     *
     * @param rel The relation type of the link.
     * @param obj The JSON object to parse for link information.
     * @param linksForRel A dictionary to add found links to. Maps hrefs to
     * titles.
     * @param linkTemplates A dictionary to add found link templates to. Maps
     * rels to templated hrefs.
     */
    private void parseLinkObject(String rel, ObjectNode obj, Map<URI, String> linksForRel, Map<String, String> linkTemplates) {
        JsonNode href = obj.findValue("href");
        if (href == null) {
            return;
        }

        JsonNode templated = obj.findValue("templated");
        if (templated != null && templated.isBoolean() && templated.asBoolean()) {
            linkTemplates.put(rel, href.asText());
        } else {
            JsonNode title = obj.findValue("title");
            linksForRel.put(
                    uri.resolve(href.asText()),
                    (title != null && title.getNodeType() == JsonNodeType.STRING) ? title.asText() : null);
        }
    }

    /**
     * Returns the element with the specified key from the map. Creates, adds
     * and returns a new element if no match was found.
     *
     * @param map The map to look in.
     * @param key The key to look for.
     * @return The existing or new element.
     */
    private static Map<URI, String> getOrAdd(Map<String, Map<URI, String>> map, String key) {
        Map<URI, String> value = map.get(key);
        if (value == null) {
            map.put(key, value = new HashMap<>());
        }
        return value;
    }

    // NOTE: Always replace entire dictionary rather than modifying it to ensure thread-safety.
    private Map<String, Map<URI, String>> links = unmodifiableMap(new HashMap<>());

    // NOTE: Only modify during initial setup
    private final Map<String, Set<URI>> defaultLinks = new HashMap<>();

    @Override
    public Set<URI> getLinks(String rel) {
        Map<URI, String> linksForRel = links.get(rel);
        if (linksForRel != null) {
            return linksForRel.keySet();
        }

        Set<URI> defaulLinksForRel = defaultLinks.get(rel);
        if (defaulLinksForRel != null) {
            return defaulLinksForRel;
        }

        return new HashSet<>();
    }

    @Override
    public Map<URI, String> getLinksWithTitles(String rel) {
        Map<URI, String> linksForRel = links.get(rel);
        if (linksForRel != null) {
            return linksForRel;
        }

        Set<URI> defaulLinksForRel = defaultLinks.get(rel);
        if (defaulLinksForRel != null) {
            Map<URI, String> result = new HashMap<>();
            defaulLinksForRel.forEach(link -> result.put(link, null));
            return result;
        }

        return new HashMap<>();
    }

    @Override
    public URI link(String rel) {
        Set<URI> linkSet = getLinks(rel);
        if (linkSet.isEmpty()) {
            // Lazy lookup
            try {
                executeAndHandle(Request.Head(uri));
            } catch (IOException | IllegalAccessException | RuntimeException ex) {
                throw new RuntimeException("No link with rel=" + rel + " provided by endpoint " + getUri() + ".", ex);
            }

            linkSet = getLinks(rel);
            if (linkSet.isEmpty()) {
                throw new RuntimeException("No link with rel=" + rel + " provided by endpoint " + getUri() + ".");
            }
        }

        return linkSet.iterator().next();
    }

    // NOTE: Always replace entire dictionary rather than modifying it to ensure thread-safety.
    private Map<String, String> linkTemplates = unmodifiableMap(new HashMap<>());

    // NOTE: Only modify during initial setup
    private final Map<String, String> defaultLinkTemplates = new HashMap<>();

    @Override
    public UriTemplate linkTemplate(String rel) {
        String template = linkTemplates.get(rel);
        if (template == null) {
            template = defaultLinkTemplates.get(rel);
        }
        if (template == null) {
            // Lazy lookup
            try {
                executeAndHandle(Request.Head(uri));
            } catch (IOException | IllegalAccessException | RuntimeException ex) {
                // HTTP HEAD server-side implementation is optional
            }

            if (linkTemplates != null) {
                template = linkTemplates.get(rel);
                if (template == null) {
                    throw new RuntimeException("No link template with rel=" + rel + " provided by endpoint " + getUri() + ".");
                }
            }
        }

        // Create new template instance for each request because UriTemplate is not immutable
        return UriTemplate.fromTemplate(template);
    }

    /**
     * Handles allowed HTTP methods and other capabilities reported by the
     * server.
     *
     * @param response The response to check for the "Allow" header.
     */
    protected void handleCapabilities(HttpResponse response) {
        allowedMethods = unmodifiableSet(stream(response.getHeaders("Allow"))
                .filter(x -> x.getName().equals("Allow"))
                .flatMap(x -> stream(x.getElements())).map(x -> x.getName())
                .collect(toSet()));
    }

    // NOTE: Always replace entire set rather than modifying it to ensure thread-safety.
    private Set<String> allowedMethods = unmodifiableSet(new HashSet<>());

    /**
     * Shows whether the server has indicated that a specific HTTP method is
     * currently allowed.
     *
     * Uses cached data from last response.
     *
     * @param method The HTTP method (e.g. GET, POST, ...) to check.
     * @return An indicator whether the method is allowed. If no request has been
     * sent yet or the server did not specify allowed methods
     * {@link Optional#empty()} is returned.
     */
    protected Optional<Boolean> isMethodAllowed(String method) {
        if (allowedMethods.isEmpty()) {
            return Optional.empty();
        }
        return Optional.of(allowedMethods.contains(method));
    }

    @Override
    public String toString() {
        return getClass().getName() + ": " + uri;
    }
}


/*
 * Copyright (c) 2002 Cunningham & Cunningham, Inc.
 * Copyright (c) 2009-2015 by Jochen Wierum & Cologne Intelligence
 *
 * This file is part of FitGoodies.
 *
 * FitGoodies is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * FitGoodies is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with FitGoodies.  If not, see <http://www.gnu.org/licenses/>.
*/

package de.cologneintelligence.fitgoodies.database.dynamic;

import de.cologneintelligence.fitgoodies.types.ScientificDouble;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * Maps a ResultSet to an Array of dynamic generated objects.
 * These objects can be read from fit.
 */
public class ResultSetWrapper {
    private final List<String> names = new ArrayList<>();
    private final List<Class<?>> types = new ArrayList<>();
    private final List<Object[]> rows = new ArrayList<>();
    private final ResultSet resultSet;
    private final int columnCount;
    private Class<?> clazz;

    private int analyzeRow() throws SQLException {
        Object[] row = new Object[columnCount];
        int result = 0;

        for (int col = 0; col < columnCount; ++col) {
            row[col] = resultSet.getObject(col + 1);

            if (types.get(col) == null && row[col] != null) {
                Class<?> c = row[col].getClass();
                if (c.equals(Float.class) || c.equals(Float.TYPE)
                    || c.equals(Double.class) || c.equals(Double.TYPE)) {
                    c = ScientificDouble.class;
                }
                types.set(col, c);
                ++result;
            }
        }
        rows.add(row);

        return result;
    }

    /**
     * Generates a new wrapper which maps {@code rs}.
     *
     * @param source {@code ResultSet} to wrap
     * @throws SQLException generated by {@code rs} while reading it.
     */
    public ResultSetWrapper(final ResultSet source) throws SQLException {
        int found = 0;

        ResultSetMetaData meta = source.getMetaData();
        columnCount = meta.getColumnCount();
        for (int i = 1; i <= columnCount; ++i) {
            types.add(null);
        }

        this.resultSet = source;
        while (found < columnCount && source.next()) {
            found += analyzeRow();
        }

        makeClass(meta);
    }

    private void makeClass(final ResultSetMetaData meta) throws SQLException {
        DynamicObjectFactory factory = new DynamicObjectFactory();
        for (int i = 1; i <= columnCount; ++i) {
            String columnName = meta.getColumnName(i);
            names.add(columnName);

            if (types.get(i - 1) == null) {
                types.set(i - 1, Object.class);
            }

            try {
                factory.add(
                       types.get(i - 1),
                        columnName);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        }
        clazz = factory.compile();
    }

    /**
     * Creates a new, empty object which has the same fields as the saved
     * {@code ResultSet}.
     *
     * @return dynamic object for one record set
     */
    public final Object createContainerObject() {
        try {
            return clazz.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Generates an array which represents the whole {@code ResultSet}.
     * Each element equates to one row in the {@code ResultSet}.
     * The elements have public fields with the same name as the {@code ResultSet} columns.
     * The {@link ScientificDouble} class is automatically used.
     * They can be read them using reflections.
     *
     * @return array of dynamic objects
     * @throws SQLException thrown, if the {@code ResultSet} throws an Exception
     */
    public final Object[] getRows() throws SQLException {
        List<Object> result = new ArrayList<>();

        while (rows.size() > 0) {
            Object[] copy = rows.remove(0);
            Object o = createContainerObject();

            for (int i = 0; i < columnCount; ++i) {
                setObjectValue(o, names.get(i), copy[i]);
            }

            result.add(o);
        }

        while (resultSet.next()) {
            Object o = createContainerObject();
            for (int col = 0; col < columnCount; ++col) {
                setObjectValue(o, names.get(col), resultSet.getObject(col + 1));
            }
            result.add(o);
        }

        return result.toArray();
    }

    private void setObjectValue(
        final Object o, final String valueName, final Object value) {
        try {
            if (mustConvertToScientificDouble(o, valueName, value)) {
                ScientificDouble sdbl = new ScientificDouble((Double) value);
                o.getClass().getField(valueName).set(o, sdbl);
            } else {
                o.getClass().getField(valueName).set(o, value);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private boolean mustConvertToScientificDouble(final Object o,
                                                  final String valueName, final Object value) throws NoSuchFieldException {
        Class<?> destClass = o.getClass().getField(valueName).getType();
        return destClass.equals(ScientificDouble.class)
            && !value.getClass().equals(ScientificDouble.class);
    }

    /**
     * Gets the class of the dynamic generated object.
     *
     * @return Class of the dynamic generated object.
     */
    public final Class<?> getClazz() {
        return clazz;
    }
}


package net.typedrest;

/**
 * Provides utility methods for {@link Throwable}.
 */
public final class ThrowableUtils {

    private ThrowableUtils() {
    }

    /**
     * Concatenates the {@link Throwable#getLocalizedMessage()}s of the entire
     * {@link Throwable#getCause()} tree.
     *
     * @param throwable The top-level throwable to walk for messages.
     * @return A newline separated list of messages.
     */
    @SuppressWarnings("ThrowableResultIgnored")
    public static String getFullMessage(Throwable throwable) {
        StringBuilder builder = new StringBuilder();
        do {
            builder.append(throwable.getLocalizedMessage()).append("\n");
            throwable = throwable.getCause();
        } while (throwable != null);
        return builder.toString();
    }
}


package js.container;

import java.net.MalformedURLException;
import java.util.HashMap;
import java.util.Map;
import java.util.ServiceLoader;

import js.core.AppFactory;
import js.lang.BugError;
import js.rmi.RemoteFactory;
import js.rmi.RemoteFactoryProvider;
import js.rmi.UnsupportedProtocolException;
import js.util.Strings;

/**
 * Managed instances factory for classes deployed remotely. Beside being an instance factory this class also implements
 * {@link RemoteFactory}, providing support for {@link AppFactory#getRemoteInstance(String, Class)}.
 *

 * RemoteInstanceFactory keeps a collection of {@link RemoteFactory} mapped by URL protocols. This class scans for
 * remote factory extensions deployed on run-time context as standard Java services, see {@link RemoteFactoryProvider}. It is
 * not legal to override an already registered protocol.
 *
 * @author Iulian Rotaru
 * @version final
 */
public class RemoteInstanceFactory implements InstanceFactory, RemoteFactory {
	/** Remote factories for specific URL protocols. */
	private Map<String, RemoteFactory> remoteFactories = new HashMap<>();

	/**
	 * Scan for {@link RemoteFactoryProvider} extensions and register {@link RemoteFactory} for provided protocols. Protocol
	 * syntax is implementation detail but is part of implementationURL supplied - as declared by provider, to
	 * {@link #getRemoteInstance(String, Class)}.
	 *
	 * @throws BugError if attempt to override already registered protocol.
	 */
	public RemoteInstanceFactory() {
		for (RemoteFactoryProvider provider : ServiceLoader.load(RemoteFactoryProvider.class)) {
			for (String protocol : provider.getProtocols()) {
				if (remoteFactories.put(protocol, provider.getRemoteFactory()) != null) {
					throw new BugError("Invalid runtime environment. Remote factory protocol override |%s|. See remote factory provider |%s|.", protocol, provider);
				}
			}
		}
	}

	/**
	 * Returns {@link InstanceType#REMOTE}, the instance type this factory instance is bound to.
	 *
	 * @return instance type this factory is bound to.
	 */
	@Override
	public InstanceType getInstanceType() {
		return InstanceType.REMOTE;
	}

	/**
	 * Create a new instance for given remote managed class. Managed class should be declared as remote into class descriptor.
	 * Also {@link ManagedClassSPI#getImplementationURL()} should be not null, that is, implementation URL should be present
	 * into class descriptor. Returned value is a Java proxy that delegates a HTTP-RMI client.
	 *

	 * This factory method does not check managed class argument validity. It should be not null and configured for remote
	 * invocation.
	 *
	 * @param managedClass managed class,
	 * @param args remote instance factory does not support constructor arguments.
	 * @param <T> managed instance type.
	 */
	@SuppressWarnings("unchecked")
	@Override
	public <T> T newInstance(ManagedClassSPI managedClass, Object... args) {
		if (args.length > 0) {
			throw new IllegalArgumentException("REMOTE instance factory does not support arguments.");
		}
		return getRemoteInstance(managedClass.getImplementationURL(), (Class<? super T>) managedClass.getInterfaceClass());
	}

	/**
	 * Alternative to {@link #newInstance(ManagedClassSPI, Object...)} when implementation URL is obtained at run-time, perhaps
	 * from user interface. Returned value is a Java proxy that delegates a HTTP-RMI client. This method is designed
	 * specifically for {@link AppFactory#getRemoteInstance(String, Class)}.
	 *

	 * This factory method does not check arguments validity. Both should be not null and interface class should be an actual
	 * Java interface.
	 *
	 * @param implementationURL the URL of remote implementation,
	 * @param interfaceClass interface implemented by remote class.
	 * @param <T> managed class implementation.
	 * @return remote class proxy instance.
	 * @throws UnsupportedProtocolException if URL protocol is not supported or arguments are otherwise not valid or null - in
	 *             which case root cause has details about erroneous argument.
	 */
	@Override
	public <T> T getRemoteInstance(String implementationURL, Class<? super T> interfaceClass) throws UnsupportedProtocolException {
		if (implementationURL == null) {
			throw new UnsupportedProtocolException(new NullPointerException("Null remote implementation URL."));
		}
		String protocol = Strings.getProtocol(implementationURL);
		if (protocol == null) {
			throw new UnsupportedProtocolException(new MalformedURLException("Protocol not found on " + implementationURL));
		}

		RemoteFactory remoteFactory = remoteFactories.get(protocol);
		if (remoteFactory == null) {
			throw new UnsupportedProtocolException("No remote factory registered for protocol |%s|.", protocol);
		}
		return remoteFactory.getRemoteInstance(implementationURL, interfaceClass);
	}
}