/** * Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com> */ package org.deephacks.confit.internal.core.property.typesafe.impl; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Map; import org.deephacks.confit.internal.core.property.typesafe.ConfigValueType; /** * Default automatic type transformations. */ final class DefaultTransformer { static AbstractConfigValue transform(AbstractConfigValue value, ConfigValueType requested) { if (value.valueType() == ConfigValueType.STRING) { String s = (String) value.unwrapped(); switch (requested) { case NUMBER: try { Long v = Long.parseLong(s); return new ConfigLong(value.origin(), v, s); } catch (NumberFormatException e) { // try Double } try { Double v = Double.parseDouble(s); return new ConfigDouble(value.origin(), v, s); } catch (NumberFormatException e) { // oh well. } break; case NULL: if (s.equals("null")) return new ConfigNull(value.origin()); break; case BOOLEAN: if (s.equals("true") || s.equals("yes") || s.equals("on")) { return new ConfigBoolean(value.origin(), true); } else if (s.equals("false") || s.equals("no") || s.equals("off")) { return new ConfigBoolean(value.origin(), false); } break; case LIST: // can't go STRING to LIST automatically break; case OBJECT: // can't go STRING to OBJECT automatically break; case STRING: // no-op STRING to STRING break; } } else if (requested == ConfigValueType.STRING) { // if we converted null to string here, then you wouldn't properly // lookup a missing-value error if you tried to lookup a null value // as a string. switch (value.valueType()) { case NUMBER: // FALL THROUGH case BOOLEAN: return new ConfigString(value.origin(), value.transformToString()); case NULL: // want to be sure this throws instead of returning "null" as a // string break; case OBJECT: // no OBJECT to STRING automatically break; case LIST: // no LIST to STRING automatically break; case STRING: // no-op STRING to STRING break; } } else if (requested == ConfigValueType.LIST && value.valueType() == ConfigValueType.OBJECT) { // attempt to convert an array-like (numeric indices) object to a // list. This would be used with .properties syntax for example: // -Dfoo.0=bar -Dfoo.1=baz // To ensure we still throw type errors for objects treated // as lists in most cases, we'll refuse to convert if the object // does not contain any numeric keys. This means we don't allow // empty objects here though :-/ AbstractConfigObject o = (AbstractConfigObject) value; Map<Integer, AbstractConfigValue> values = new HashMap<Integer, AbstractConfigValue>(); for (String key : o.keySet()) { int i; try { i = Integer.parseInt(key, 10); if (i < 0) continue; values.put(i, o.get(key)); } catch (NumberFormatException e) { continue; } } if (!values.isEmpty()) { ArrayList<Map.Entry<Integer, AbstractConfigValue>> entryList = new ArrayList<Map.Entry<Integer, AbstractConfigValue>>( values.entrySet()); // sort by numeric index Collections.sort(entryList, new Comparator<Map.Entry<Integer, AbstractConfigValue>>() { @Override public int compare(Map.Entry<Integer, AbstractConfigValue> a, Map.Entry<Integer, AbstractConfigValue> b) { // Integer.compare was added in 1.7 so not using // it here yet return Integer.valueOf(a.getKey()).compareTo(b.getKey()); } }); // drop the indices (we allow gaps in the indices, for better or // worse) ArrayList<AbstractConfigValue> list = new ArrayList<AbstractConfigValue>(); for (Map.Entry<Integer, AbstractConfigValue> entry : entryList) { list.add(entry.getValue()); } return new SimpleConfigList(value.origin(), list); } } return value; } }