Util.java

package org.microspace.table.query.sql;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;

import org.microspace.util.PojoUtil;
import org.microspace.util.UniqueId;

/**
 * Utility package for MicroSpace.
 * 
 * @author Gaspar Sinai - {@literal gaspar.sinai@microspace.org}
 * @version 2017-09-29
 */
public class Util {
	/* Almost ISO 8601 international standard date and time notation */
	public static final String FORMAT_DATE = "yyyy-MM-dd";
	public static final String FORMAT_DATE_TIME = "yyyy-MM-dd HH:mm:ss";
	public static final String FORMAT_DATE_TIME_MS = "yyyy-MM-dd HH:mm:ss.SSS";

	private static ThreadLocal<SimpleDateFormat> dateFormatterCacheDate = new ThreadLocal<SimpleDateFormat> () {
		@Override 
		public SimpleDateFormat initialValue () {
			SimpleDateFormat ret = new SimpleDateFormat(FORMAT_DATE);
			ret.setTimeZone(TimeZone.getTimeZone("UTC"));
			return ret;
		}
	};
	private static ThreadLocal<SimpleDateFormat> dateFormatterCacheDateTime = new ThreadLocal<SimpleDateFormat> () {
		@Override 
		public SimpleDateFormat initialValue () {
			SimpleDateFormat ret = new SimpleDateFormat(FORMAT_DATE_TIME);
			ret.setTimeZone(TimeZone.getTimeZone("UTC"));
			return ret;
		}
	};
	private static ThreadLocal<SimpleDateFormat> dateFormatterCacheDateTimeMs = new ThreadLocal<SimpleDateFormat> () {
		@Override 
		public SimpleDateFormat initialValue () {
			SimpleDateFormat ret = new SimpleDateFormat(FORMAT_DATE_TIME_MS);
			ret.setTimeZone(TimeZone.getTimeZone("UTC"));
			return ret;
		}
	};
	
	public static Date parseDate (String value) {
		try {
			if (value.length() == FORMAT_DATE_TIME_MS.length()) {
				return dateFormatterCacheDateTimeMs.get().parse(value);
			} else if (value.length() == FORMAT_DATE_TIME.length()) {
				return dateFormatterCacheDateTime.get().parse(value);
			} else if (value.length() == FORMAT_DATE.length()) {
				return dateFormatterCacheDate.get().parse(value);
			} else {
				throw new IllegalArgumentException ("Can Not Guess Date Format");
			}
		} catch (ParseException e) {
			throw new IllegalArgumentException("Bad Date Format", e);
		}
	}
	
	/**
	 * Remove quote signs and resolve escape sequences.
	 * <pre>
	 * 'single=\'' or '' backslash=\\' = single=' or ' backslash=\
	 * 
	 * "double=\" or "" backslash=\" =  double=" or "" backslash=\ 
	 * </pre>
	 * @param quotedString The quoted String.
	 * @return The plain string.
	 */
	public static String unquote (String quotedString) {
		char begin = quotedString.charAt(0);
		char end = quotedString.charAt(quotedString.length()-1);
		if (begin != end) {
			throw new IllegalArgumentException ("Malformed String");
		}
		StringBuffer ret = new StringBuffer(quotedString.length());
		for (int i=1; i<quotedString.length()-1; i++) {
			if (quotedString.charAt(i) == '\\'	&&
					(quotedString.charAt(i+1) == begin || quotedString.charAt(i+1) == '\\')) 
			{
				ret.append(quotedString.charAt(i+1));
				i++;
				if (i>=quotedString.length()-1) {
					throw new IllegalArgumentException ("Malformed String Too Short for '\\'");
				}	
				continue;
			}
			if (quotedString.charAt(i) == begin	&&
					quotedString.charAt(i+1) == begin
					) {
				ret.append(begin);
				i++;
				if (i>=quotedString.length()-1) {
					throw new IllegalArgumentException ("Malformed String Too Short");
				}	
				continue;
			}
			ret.append(quotedString.charAt(i));
		}
		return ret.toString();
	}
	
	/**
	 * Quote a bareString.
	 * @param bareString The bare String.
	 * @return The quoted string suitable for Sql.
	 */
	public static String quote (String bareString) {
		StringBuffer ret = new StringBuffer(bareString.length());
		ret.append("'");
		for (int i=0; i<bareString.length(); i++) {
			char at = bareString.charAt(i);
			if (at == '\\') {
				ret.append("\\\\");
				continue;
			}		
			if (at == '\'') {
				ret.append("\\\'");
				continue;
			}
			ret.append(at);
		}
		ret.append("'");
		return ret.toString();
	}
	
	@SuppressWarnings("unchecked")
	public static <T> T createColumnObject (String value, 
			Class<T> columnClass, LiteralValueType columnType) {
		T columnObject;
		if (Date.class.isAssignableFrom(columnClass)) {
			columnObject = (T) Util.parseDate(value);
		} else {
			columnObject = PojoUtil.createObjectFromString(value, columnClass);
			if (columnObject == null) {
				throw new IllegalArgumentException("Can not create class from String " + columnClass);
			}
			if (columnType == LiteralValueType.BOOLEAN && ! (columnObject instanceof Boolean)) {
			throw new IllegalArgumentException("Boolean Type Expected");
			}
			if (columnType == LiteralValueType.NUMBER && columnObject instanceof String) {
				throw new IllegalArgumentException("Numeric Type Expected");
			}
			if (columnType == LiteralValueType.NUMBER && columnObject instanceof UniqueId) {
				throw new IllegalArgumentException("Numeric Type Expected");
			}
		}
		return columnObject;
	}
	
}