brooklyn-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From henev...@apache.org
Subject [05/10] brooklyn-server git commit: refactor TypeCoercions so that most is in utils with an interface
Date Thu, 07 Jul 2016 09:18:39 GMT
refactor TypeCoercions so that most is in utils with an interface

with the Brooklyn TypeCoercions referring to and extending that


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/ec4da197
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/ec4da197
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/ec4da197

Branch: refs/heads/master
Commit: ec4da197d5caf3ac9bb63ab94924718aae83c615
Parents: 23ab1c5
Author: Alex Heneveld <alex.heneveld@cloudsoftcorp.com>
Authored: Fri Jun 24 11:02:52 2016 +0100
Committer: Alex Heneveld <alex.heneveld@cloudsoftcorp.com>
Committed: Fri Jun 24 23:02:48 2016 +0100

----------------------------------------------------------------------
 .../spi/dsl/methods/BrooklynDslCommon.java      |   2 +-
 .../util/core/flags/ClassCoercionException.java |  41 -
 .../brooklyn/util/core/flags/TypeCoercions.java | 908 +++----------------
 .../util/core/internal/TypeCoercionsTest.java   |  11 +-
 .../rest/util/DefaultExceptionMapper.java       |   2 +-
 .../javalang/coerce/ClassCoercionException.java |  48 +
 .../util/javalang/coerce/CoerceFunctionals.java |  41 +
 .../coerce/CommonAdaptorTypeCoercions.java      | 380 ++++++++
 .../util/javalang/coerce/EnumTypeCoercions.java | 104 +++
 .../coerce/PrimitiveStringTypeCoercions.java    | 208 +++++
 .../util/javalang/coerce/TypeCoercer.java       |  31 +
 .../javalang/coerce/TypeCoercerExtensible.java  | 296 ++++++
 .../util/javalang/coerce/TypeCoercionsTest.java | 379 ++++++++
 13 files changed, 1625 insertions(+), 826 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/ec4da197/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
index b88829b..309785d 100644
--- a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
+++ b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
@@ -46,13 +46,13 @@ import org.apache.brooklyn.core.mgmt.persist.DeserializingClassRenamesProvider;
 import org.apache.brooklyn.core.sensor.DependentConfiguration;
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.core.config.ConfigBag;
-import org.apache.brooklyn.util.core.flags.ClassCoercionException;
 import org.apache.brooklyn.util.core.flags.FlagUtils;
 import org.apache.brooklyn.util.core.flags.TypeCoercions;
 import org.apache.brooklyn.util.core.task.DeferredSupplier;
 import org.apache.brooklyn.util.core.task.Tasks;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.javalang.Reflections;
+import org.apache.brooklyn.util.javalang.coerce.ClassCoercionException;
 import org.apache.brooklyn.util.text.StringEscapes.JavaStringEscapes;
 import org.apache.brooklyn.util.text.Strings;
 import org.apache.commons.beanutils.BeanUtils;

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/ec4da197/core/src/main/java/org/apache/brooklyn/util/core/flags/ClassCoercionException.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/util/core/flags/ClassCoercionException.java b/core/src/main/java/org/apache/brooklyn/util/core/flags/ClassCoercionException.java
deleted file mode 100644
index cea7484..0000000
--- a/core/src/main/java/org/apache/brooklyn/util/core/flags/ClassCoercionException.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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 org.apache.brooklyn.util.core.flags;
-
-/**
- * Thrown to indicate that {@link TypeCoercions} could not cast an object from one
- * class to another.
- */
-public class ClassCoercionException extends ClassCastException {
-    private static final long serialVersionUID = -4616045237993172497L;
-
-    public ClassCoercionException() {
-        super();
-    }
-
-    /**
-     * Constructs a <code>ClassCoercionException</code> with the specified
-     * detail message.
-     *
-     * @param s the detail message.
-     */
-    public ClassCoercionException(String s) {
-        super(s);
-    }
-}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/ec4da197/core/src/main/java/org/apache/brooklyn/util/core/flags/TypeCoercions.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/util/core/flags/TypeCoercions.java b/core/src/main/java/org/apache/brooklyn/util/core/flags/TypeCoercions.java
index 65f85d6..0169827 100644
--- a/core/src/main/java/org/apache/brooklyn/util/core/flags/TypeCoercions.java
+++ b/core/src/main/java/org/apache/brooklyn/util/core/flags/TypeCoercions.java
@@ -22,26 +22,9 @@ import groovy.lang.Closure;
 import groovy.time.TimeDuration;
 
 import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.net.InetAddress;
-import java.net.URI;
-import java.net.URL;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Date;
-import java.util.List;
 import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
 
 import javax.annotation.Nullable;
-import javax.annotation.concurrent.GuardedBy;
 
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.sensor.AttributeSensor;
@@ -53,443 +36,177 @@ import org.apache.brooklyn.core.internal.BrooklynInitialization;
 import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
 import org.apache.brooklyn.core.sensor.Sensors;
 import org.apache.brooklyn.util.JavaGroovyEquivalents;
-import org.apache.brooklyn.util.collections.MutableSet;
-import org.apache.brooklyn.util.collections.QuorumCheck;
-import org.apache.brooklyn.util.collections.QuorumCheck.QuorumChecks;
 import org.apache.brooklyn.util.core.task.Tasks;
-import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.guava.Maybe;
-import org.apache.brooklyn.util.javalang.Enums;
+import org.apache.brooklyn.util.javalang.Boxing;
 import org.apache.brooklyn.util.javalang.JavaClassNames;
-import org.apache.brooklyn.util.net.Cidr;
-import org.apache.brooklyn.util.net.Networking;
-import org.apache.brooklyn.util.net.UserAndHostAndPort;
-import org.apache.brooklyn.util.text.StringEscapes.JavaStringEscapes;
-import org.apache.brooklyn.util.text.Strings;
-import org.apache.brooklyn.util.time.Duration;
-import org.apache.brooklyn.util.time.Time;
-import org.apache.brooklyn.util.yaml.Yamls;
+import org.apache.brooklyn.util.javalang.Reflections;
+import org.apache.brooklyn.util.javalang.coerce.CommonAdaptorTypeCoercions;
+import org.apache.brooklyn.util.javalang.coerce.EnumTypeCoercions;
+import org.apache.brooklyn.util.javalang.coerce.PrimitiveStringTypeCoercions;
+import org.apache.brooklyn.util.javalang.coerce.TypeCoercer;
+import org.apache.brooklyn.util.javalang.coerce.TypeCoercerExtensible;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.CaseFormat;
 import com.google.common.base.Function;
-import com.google.common.base.Objects;
-import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
-import com.google.common.collect.HashBasedTable;
-import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import com.google.common.collect.Table;
-import com.google.common.net.HostAndPort;
-import com.google.common.primitives.Primitives;
 import com.google.common.reflect.TypeToken;
 
-@SuppressWarnings("rawtypes")
+/** Static class providing a shared {@link TypeCoercer} for all of Brooklyn */
 public class TypeCoercions {
 
     private static final Logger log = LoggerFactory.getLogger(TypeCoercions.class);
     
     private TypeCoercions() {}
 
-    /** Store the coercion {@link Function functions} in a {@link Table table}. */
-    @GuardedBy("TypeCoercions.class")
-    private static Table<Class, Class, Function> registry = HashBasedTable.create();
-
-    /**
-     * Attempts to coerce {@code value} to {@code targetType}.
-     * <p>
-     * Maintains a registry of adapter functions for type pairs in a {@link Table} which
-     * is searched after checking various strategies, including the following:
-     * <ul>
-     * <li>{@code value.asTargetType()}
-     * <li>{@code TargetType.fromType(value)} (if {@code value instanceof Type})
-     * <li>{@code value.targetTypeValue()} (handy for primitives)
-     * <li>{@code TargetType.valueOf(value)} (for enums)
-     * </ul>
-     * <p>
-     * A default set of adapters will handle most common Java-type coercions
-     * as well as <code>String</code> coercion to:
-     * <ul>
-     * <li> {@link Set}, {@link List}, {@link Map} and similar -- parses as YAML
-     * <li> {@link Date} -- parses using {@link Time#parseDate(String)}
-     * <li> {@link Duration} -- parses using {@link Duration#parse(String)}
-     * </ul>
-     */
-    public static <T> T coerce(Object value, Class<T> targetType) {
-        return coerce(value, TypeToken.of(targetType));
+    static TypeCoercerExtensible coercer;
+    static {
+        coercer = TypeCoercerExtensible.newEmpty();
+        BrooklynInitialization.initTypeCoercionStandardAdapters(); 
+    }
+    
+    public static void initStandardAdapters() {
+        new CommonAdaptorTypeCoercions(coercer).registerAllAdapters();
+        registerDeprecatedBrooklynAdapters();
+        registerBrooklynAdapters();
+        registerGroovyAdapters();
     }
+    
+    public static <T> T coerce(Object input, Class<T> type) { return coercer.coerce(input, type); }
+    public static <T> T coerce(Object input, TypeToken<T> type) { return coercer.coerce(input, type); }
+    public static <T> Maybe<T> tryCoerce(Object input, Class<T> type) { return coercer.tryCoerce(input, type); }
+    public static <T> Maybe<T> tryCoerce(Object input, TypeToken<T> type) { return coercer.tryCoerce(input, type); }
 
-    /** @see #coerce(Object, Class); allows a null value in the contents of the Maybe */
-    public static <T> Maybe<T> tryCoerce(Object value, TypeToken<T> targetTypeToken) {
-        try {
-            return Maybe.ofAllowingNull( coerce(value, targetTypeToken) );
-        } catch (Throwable t) {
-            Exceptions.propagateIfFatal(t);
-            return Maybe.absent(t); 
-        }
+    public static <A,B> Function<? super A,B> registerAdapter(Class<A> sourceType, Class<B> targetType, Function<? super A,B> fn) {
+        return coercer.registerAdapter(sourceType, targetType, fn);
     }
     
-    /** @see #coerce(Object, Class) */
-    @SuppressWarnings({ "unchecked" })
-    public static <T> T coerce(Object value, TypeToken<T> targetTypeToken) {
-        if (value==null) return null;
-        Class<? super T> targetType = targetTypeToken.getRawType();
+    public static <T> Function<Object, T> function(final Class<T> type) {
+        return coercer.function(type);
+    }
 
-        //recursive coercion of parameterized collections and map entries
-        if (targetTypeToken.getType() instanceof ParameterizedType) {
-            if (value instanceof Collection && Collection.class.isAssignableFrom(targetType)) {
-                Type[] arguments = ((ParameterizedType) targetTypeToken.getType()).getActualTypeArguments();
-                if (arguments.length != 1) {
-                    throw new IllegalStateException("Unexpected number of parameters in collection type: " + arguments);
-                }
-                Collection coerced = Lists.newLinkedList();
-                TypeToken<?> listEntryType = TypeToken.of(arguments[0]);
-                for (Object entry : (Iterable<?>) value) {
-                    coerced.add(coerce(entry, listEntryType));
-                }
-                if (Set.class.isAssignableFrom(targetType)) {
-                    return (T) Sets.newLinkedHashSet(coerced);
-                } else {
-                    return (T) Lists.newArrayList(coerced);
-                }
-            } else if (value instanceof Map && Map.class.isAssignableFrom(targetType)) {
-                Type[] arguments = ((ParameterizedType) targetTypeToken.getType()).getActualTypeArguments();
-                if (arguments.length != 2) {
-                    throw new IllegalStateException("Unexpected number of parameters in map type: " + arguments);
-                }
-                Map coerced = Maps.newLinkedHashMap();
-                TypeToken<?> mapKeyType = TypeToken.of(arguments[0]);
-                TypeToken<?> mapValueType = TypeToken.of(arguments[1]);
-                for (Map.Entry entry : ((Map<?,?>) value).entrySet()) {
-                    coerced.put(coerce(entry.getKey(), mapKeyType),  coerce(entry.getValue(), mapValueType));
-                }
-                return (T) Maps.newLinkedHashMap(coerced);
+    @SuppressWarnings({"unused", "deprecation", "unchecked", "rawtypes"})
+    public static void registerDeprecatedBrooklynAdapters() {
+        registerAdapter(Closure.class, ConfigurableEntityFactory.class, new Function<Closure,ConfigurableEntityFactory>() {
+            @Override
+            public ConfigurableEntityFactory apply(Closure input) {
+                return new ClosureEntityFactory(input);
             }
-        }
-
-        if (targetType.isInstance(value)) return (T) value;
-
-        // TODO use registry first?
-
-        //deal with primitive->primitive casting
-        if (isPrimitiveOrBoxer(targetType) && isPrimitiveOrBoxer(value.getClass())) {
-            // Don't just rely on Java to do its normal casting later; if caller writes
-            // long `l = coerce(new Integer(1), Long.class)` then letting java do its casting will fail,
-            // because an Integer will not automatically be unboxed and cast to a long
-            return castPrimitive(value, (Class<T>)targetType);
-        }
-
-        //deal with string->primitive
-        if (value instanceof String && isPrimitiveOrBoxer(targetType)) {
-            return stringToPrimitive((String)value, (Class<T>)targetType);
-        }
-
-        //deal with primitive->string
-        if (isPrimitiveOrBoxer(value.getClass()) && targetType.equals(String.class)) {
-            return (T) value.toString();
-        }
-
-        //look for value.asType where Type is castable to targetType
-        String targetTypeSimpleName = getVerySimpleName(targetType);
-        if (targetTypeSimpleName!=null && targetTypeSimpleName.length()>0) {
-            for (Method m: value.getClass().getMethods()) {
-                if (m.getName().startsWith("as") && m.getParameterTypes().length==0 &&
-                        targetType.isAssignableFrom(m.getReturnType()) ) {
-                    if (m.getName().equals("as"+getVerySimpleName(m.getReturnType()))) {
-                        try {
-                            return (T) m.invoke(value);
-                        } catch (Exception e) {
-                            throw new ClassCoercionException("Cannot coerce type "+value.getClass()+" to "+targetType.getCanonicalName()+" ("+value+"): "+m.getName()+" adapting failed, "+e);
-                        }
-                    }
+        });
+        Function<?,?> ignoredVarHereToAllowSuppressDeprecationWarning1 = registerAdapter(org.apache.brooklyn.core.entity.factory.EntityFactory.class, ConfigurableEntityFactory.class, new Function<org.apache.brooklyn.core.entity.factory.EntityFactory,ConfigurableEntityFactory>() {
+            @Override
+            public ConfigurableEntityFactory apply(org.apache.brooklyn.core.entity.factory.EntityFactory input) {
+                if (input instanceof ConfigurableEntityFactory) return (ConfigurableEntityFactory)input;
+                return new ConfigurableEntityFactoryFromEntityFactory(input);
+            }
+        });
+        Function<?,?> ignoredVarHereToAllowSuppressDeprecationWarning2 = registerAdapter(Closure.class, org.apache.brooklyn.core.entity.factory.EntityFactory.class, new Function<Closure,org.apache.brooklyn.core.entity.factory.EntityFactory>() {
+            @Override
+            public org.apache.brooklyn.core.entity.factory.EntityFactory apply(Closure input) {
+                return new ClosureEntityFactory(input);
+            }
+        });
+    }
+    
+    @SuppressWarnings("rawtypes")
+    public static void registerBrooklynAdapters() {
+        registerAdapter(String.class, AttributeSensor.class, new Function<String,AttributeSensor>() {
+            @Override
+            public AttributeSensor apply(final String input) {
+                Entity entity = BrooklynTaskTags.getContextEntity(Tasks.current());
+                if (entity!=null) {
+                    Sensor<?> result = entity.getEntityType().getSensor(input);
+                    if (result instanceof AttributeSensor) 
+                        return (AttributeSensor) result;
                 }
+                return Sensors.newSensor(Object.class, input);
             }
-        }
-        
-        //now look for static TargetType.fromType(Type t) where value instanceof Type  
-        for (Method m: targetType.getMethods()) {
-            if (((m.getModifiers()&Modifier.STATIC)==Modifier.STATIC) && 
-                    m.getName().startsWith("from") && m.getParameterTypes().length==1 &&
-                    m.getParameterTypes()[0].isInstance(value)) {
-                if (m.getName().equals("from"+getVerySimpleName(m.getParameterTypes()[0]))) {
-                    try {
-                        return (T) m.invoke(null, value);
-                    } catch (Exception e) {
-                        throw new ClassCoercionException("Cannot coerce type "+value.getClass()+" to "+targetType.getCanonicalName()+" ("+value+"): "+m.getName()+" adapting failed, "+e);
-                    }
+        });
+        registerAdapter(String.class, Sensor.class, new Function<String,Sensor>() {
+            @Override
+            public AttributeSensor apply(final String input) {
+                Entity entity = BrooklynTaskTags.getContextEntity(Tasks.current());
+                if (entity!=null) {
+                    Sensor<?> result = entity.getEntityType().getSensor(input);
+                    if (result != null) 
+                        return (AttributeSensor) result;
                 }
+                return Sensors.newSensor(Object.class, input);
             }
-        }
-        
-       //ENHANCEMENT could look in type hierarchy of both types for a conversion method...
-        
-        //primitives get run through again boxed up
-        Class boxedT = UNBOXED_TO_BOXED_TYPES.get(targetType);
-        Class boxedVT = UNBOXED_TO_BOXED_TYPES.get(value.getClass());
-        if (boxedT!=null || boxedVT!=null) {
-            try {
-                if (boxedT==null) boxedT=targetType;
-                Object boxedV;
-                if (boxedVT==null) { boxedV = value; }
-                else { boxedV = boxedVT.getConstructor(value.getClass()).newInstance(value); }
-                return (T) coerce(boxedV, boxedT);
-            } catch (Exception e) {
-                throw new ClassCoercionException("Cannot coerce type "+value.getClass()+" to "+targetType.getCanonicalName()+" ("+value+"): unboxing failed, "+e);
+        });
+    }
+    
+    @SuppressWarnings("rawtypes")
+    public static void registerGroovyAdapters() {
+        registerAdapter(Closure.class, Predicate.class, new Function<Closure,Predicate>() {
+            @Override
+            public Predicate<?> apply(final Closure closure) {
+                return new Predicate<Object>() {
+                    @Override public boolean apply(Object input) {
+                        return (Boolean) closure.call(input);
+                    }
+                };
             }
-        }
-
-        //for enums call valueOf with the string representation of the value
-        if (targetType.isEnum()) {
-            T result = (T) stringToEnum((Class<Enum>) targetType, null).apply(String.valueOf(value));
-            if (result != null) return result;
-        }
-
-        //now look in registry
-        synchronized (TypeCoercions.class) {
-            Map<Class, Function> adapters = registry.row(targetType);
-            for (Map.Entry<Class, Function> entry : adapters.entrySet()) {
-                if (entry.getKey().isInstance(value)) {
-                    T result = (T) entry.getValue().apply(value);
-                    
-                    // Check if need to unwrap again (e.g. if want List<Integer> and are given a String "1,2,3"
-                    // then we'll have so far converted to List.of("1", "2", "3"). Call recursively.
-                    // First check that value has changed, to avoid stack overflow!
-                    if (!Objects.equal(value, result) && targetTypeToken.getType() instanceof ParameterizedType) {
-                        // Could duplicate check for `result instanceof Collection` etc; but recursive call
-                        // will be fine as if that doesn't match we'll safely reach `targetType.isInstance(value)`
-                        // and just return the result.
-                        return coerce(result, targetTypeToken);
+        });
+        registerAdapter(Closure.class, Function.class, new Function<Closure,Function>() {
+            @Override
+            public Function apply(final Closure closure) {
+                return new Function() {
+                    @Override public Object apply(Object input) {
+                        return closure.call(input);
                     }
-                    return result;
-                }
+                };
             }
-        }
-
-        //not found
-        if (targetType.isEnum()) {
-            try {
-                throw new ClassCoercionException("Invalid value '"+value+"' for "+JavaClassNames.simpleClassName(targetType)+"; expected one of "+
-                    Arrays.asList((Object[])targetType.getMethod("values").invoke(null)));
-            } catch (ClassCoercionException e) {
-                throw e;
-            } catch (Exception e) {
-                Exceptions.propagateIfFatal(e);
-                // fall back to below
+        });
+        registerAdapter(Object.class, TimeDuration.class, new Function<Object,TimeDuration>() {
+            @SuppressWarnings("deprecation")
+            @Override
+            public TimeDuration apply(final Object input) {
+                log.warn("deprecated automatic coercion of Object to TimeDuration (set breakpoint in TypeCoercions to inspect, convert to Duration)");
+                return JavaGroovyEquivalents.toTimeDuration(input);
             }
-        }
-        throw new ClassCoercionException("Cannot coerce type "+value.getClass().getCanonicalName()+" to "+targetType.getCanonicalName()+" ("+value+"): no adapter known");
-    }
-
-    /**
-     * Returns a function that does a type coercion to the given type. For example,
-     * {@code TypeCoercions.function(Double.class)} will return a function that will
-     * coerce its input value to a {@link Double} (or throw a {@link ClassCoercionException}
-     * if that is not possible).
-     */
-    public static <T> Function<Object, T> function(final Class<T> type) {
-        return new CoerceFunction<T>(type);
+        });
+        registerAdapter(TimeDuration.class, Long.class, new Function<TimeDuration,Long>() {
+            @Override
+            public Long apply(final TimeDuration input) {
+                log.warn("deprecated automatic coercion of TimeDuration to Long (set breakpoint in TypeCoercions to inspect, use Duration instead of Long!)");
+                return input.toMilliseconds();
+            }
+        });
     }
-    
-    private static class CoerceFunction<T> implements Function<Object, T> {
-        private final Class<T> type;
 
-        public CoerceFunction(Class<T> type) {
-            this.type = type;
-        }
-        @Override
-        public T apply(Object input) {
-            return coerce(input, type);
-        }
-    }
+    // ---- legacy compatibility
 
-    /**
-     * Type coercion {@link Function function} for {@link Enum enums}.
-     * <p>
-     * Tries to convert the string to {@link CaseFormat#UPPER_UNDERSCORE} first,
-     * handling all of the different {@link CaseFormat format} possibilites. Failing 
-     * that, it tries a case-insensitive comparison with the valid enum values.
-     * <p>
-     * Returns {@code defaultValue} if the string cannot be converted.
-     *
-     * @see TypeCoercions#coerce(Object, Class)
-     * @see Enum#valueOf(Class, String)
-     */
+    /** @deprecated since 0.10.0 see method in {@link EnumTypeCoercions} */ @Deprecated
     public static <E extends Enum<E>> Function<String, E> stringToEnum(final Class<E> type, @Nullable final E defaultValue) {
-        return new StringToEnumFunction<E>(type, defaultValue);
+        return EnumTypeCoercions.stringToEnum(type, defaultValue);
     }
-    
-    private static class StringToEnumFunction<E extends Enum<E>> implements Function<String, E> {
-        private final Class<E> type;
-        private final E defaultValue;
         
-        public StringToEnumFunction(Class<E> type, @Nullable E defaultValue) {
-            this.type = type;
-            this.defaultValue = defaultValue;
-        }
-        @Override
-        public E apply(String input) {
-            Preconditions.checkNotNull(input, "input");
-            List<String> options = ImmutableList.of(
-                    input,
-                    CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_UNDERSCORE, input),
-                    CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_UNDERSCORE, input),
-                    CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, input),
-                    CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, input));
-            for (String value : options) {
-                try {
-                    return Enum.valueOf(type, value);
-                } catch (IllegalArgumentException iae) {
-                    continue;
-                }
-            }
-            Maybe<E> result = Enums.valueOfIgnoreCase(type, input);
-            return (result.isPresent()) ? result.get() : defaultValue;
-        }
-    }
-
-    /**
-     * Sometimes need to explicitly cast primitives, rather than relying on Java casting.
-     * For example, when using generics then type-erasure means it doesn't actually cast,
-     * which causes tests to fail with 0 != 0.0
-     */
-    @SuppressWarnings("unchecked")
+    /** @deprecated since 0.10.0 see method in {@link PrimitiveStringTypeCoercions} */ @Deprecated
     public static <T> T castPrimitive(Object value, Class<T> targetType) {
-        if (value==null) return null;
-        assert isPrimitiveOrBoxer(targetType) : "targetType="+targetType;
-        assert isPrimitiveOrBoxer(value.getClass()) : "value="+targetType+"; valueType="+value.getClass();
-
-        Class<?> sourceWrapType = Primitives.wrap(value.getClass());
-        Class<?> targetWrapType = Primitives.wrap(targetType);
-        
-        // optimization, for when already correct type
-        if (sourceWrapType == targetWrapType) {
-            return (T) value;
-        }
-        
-        if (targetWrapType == Boolean.class) {
-            // only char can be mapped to boolean
-            // (we could say 0=false, nonzero=true, but there is no compelling use case so better
-            // to encourage users to write as boolean)
-            if (sourceWrapType == Character.class)
-                return (T) stringToPrimitive(value.toString(), targetType);
-            
-            throw new ClassCoercionException("Cannot cast "+sourceWrapType+" ("+value+") to "+targetType);
-        } else if (sourceWrapType == Boolean.class) {
-            // boolean can't cast to anything else
-            
-            throw new ClassCoercionException("Cannot cast "+sourceWrapType+" ("+value+") to "+targetType);
-        }
-        
-        // for whole-numbers (where casting to long won't lose anything)...
-        long v = 0;
-        boolean islong = true;
-        if (sourceWrapType == Character.class) {
-            v = (long) ((Character)value).charValue();
-        } else if (sourceWrapType == Byte.class) {
-            v = (long) ((Byte)value).byteValue();
-        } else if (sourceWrapType == Short.class) {
-            v = (long) ((Short)value).shortValue();
-        } else if (sourceWrapType == Integer.class) {
-            v = (long) ((Integer)value).intValue();
-        } else if (sourceWrapType == Long.class) {
-            v = ((Long)value).longValue();
-        } else {
-            islong = false;
-        }
-        if (islong) {
-            if (targetWrapType == Character.class) return (T) Character.valueOf((char)v); 
-            if (targetWrapType == Byte.class) return (T) Byte.valueOf((byte)v); 
-            if (targetWrapType == Short.class) return (T) Short.valueOf((short)v); 
-            if (targetWrapType == Integer.class) return (T) Integer.valueOf((int)v); 
-            if (targetWrapType == Long.class) return (T) Long.valueOf((long)v); 
-            if (targetWrapType == Float.class) return (T) Float.valueOf((float)v); 
-            if (targetWrapType == Double.class) return (T) Double.valueOf((double)v);
-            throw new IllegalStateException("Unexpected: sourceType="+sourceWrapType+"; targetType="+targetWrapType);
-        }
-        
-        // for real-numbers (cast to double)...
-        double d = 0;
-        boolean isdouble = true;
-        if (sourceWrapType == Float.class) {
-            d = (double) ((Float)value).floatValue();
-        } else if (sourceWrapType == Double.class) {
-            d = (double) ((Double)value).doubleValue();
-        } else {
-            isdouble = false;
-        }
-        if (isdouble) {
-            if (targetWrapType == Character.class) return (T) Character.valueOf((char)d); 
-            if (targetWrapType == Byte.class) return (T) Byte.valueOf((byte)d); 
-            if (targetWrapType == Short.class) return (T) Short.valueOf((short)d); 
-            if (targetWrapType == Integer.class) return (T) Integer.valueOf((int)d); 
-            if (targetWrapType == Long.class) return (T) Long.valueOf((long)d); 
-            if (targetWrapType == Float.class) return (T) Float.valueOf((float)d); 
-            if (targetWrapType == Double.class) return (T) Double.valueOf((double)d);
-            throw new IllegalStateException("Unexpected: sourceType="+sourceWrapType+"; targetType="+targetWrapType);
-        } else {
-            throw new IllegalStateException("Unexpected: sourceType="+sourceWrapType+"; targetType="+targetWrapType);
-        }
+        return PrimitiveStringTypeCoercions.castPrimitive(value, targetType);
     }
     
+    /** @deprecated since 0.10.0 see method in {@link PrimitiveStringTypeCoercions} */ @Deprecated
     public static boolean isPrimitiveOrBoxer(Class<?> type) {
-        return Primitives.allPrimitiveTypes().contains(type) || Primitives.allWrapperTypes().contains(type);
+        return PrimitiveStringTypeCoercions.isPrimitiveOrBoxer(type);
     }
-    
-    @SuppressWarnings("unchecked")
+
+    /** @deprecated since 0.10.0 see method in {@link PrimitiveStringTypeCoercions} */ @Deprecated
     public static <T> T stringToPrimitive(String value, Class<T> targetType) {
-        assert Primitives.allPrimitiveTypes().contains(targetType) || Primitives.allWrapperTypes().contains(targetType) : "targetType="+targetType;
-        // If char, then need to do explicit conversion
-        if (targetType == Character.class || targetType == char.class) {
-            if (value.length() == 1) {
-                return (T) (Character) value.charAt(0);
-            } else if (value.length() != 1) {
-                throw new ClassCoercionException("Cannot coerce type String to "+targetType.getCanonicalName()+" ("+value+"): adapting failed");
-            }
-        }
-        value = value.trim();
-        // For boolean we could use valueOf, but that returns false whereas we'd rather throw errors on bad values
-        if (targetType == Boolean.class || targetType == boolean.class) {
-            if ("true".equalsIgnoreCase(value)) return (T) Boolean.TRUE;
-            if ("false".equalsIgnoreCase(value)) return (T) Boolean.FALSE;
-            if ("yes".equalsIgnoreCase(value)) return (T) Boolean.TRUE;
-            if ("no".equalsIgnoreCase(value)) return (T) Boolean.FALSE;
-            if ("t".equalsIgnoreCase(value)) return (T) Boolean.TRUE;
-            if ("f".equalsIgnoreCase(value)) return (T) Boolean.FALSE;
-            if ("y".equalsIgnoreCase(value)) return (T) Boolean.TRUE;
-            if ("n".equalsIgnoreCase(value)) return (T) Boolean.FALSE;
-            
-            throw new ClassCoercionException("Cannot coerce type String to "+targetType.getCanonicalName()+" ("+value+"): adapting failed"); 
-        }
-        
-        // Otherwise can use valueOf reflectively
-        Class<?> wrappedType;
-        if (Primitives.allPrimitiveTypes().contains(targetType)) {
-            wrappedType = Primitives.wrap(targetType);
-        } else {
-            wrappedType = targetType;
-        }
-        
-        try {
-            return (T) wrappedType.getMethod("valueOf", String.class).invoke(null, value);
-        } catch (Exception e) {
-            ClassCoercionException tothrow = new ClassCoercionException("Cannot coerce "+JavaStringEscapes.wrapJavaString(value)+" to "+targetType.getCanonicalName()+" ("+value+"): adapting failed");
-            tothrow.initCause(e);
-            throw tothrow;
-        }
+        return PrimitiveStringTypeCoercions.stringToPrimitive(value, targetType);
     }
     
-    /** returns the simple class name, and for any inner class the portion after the $ */
+    /** @deprecated since 0.10.0 see {@link JavaClassNames#verySimpleClassName(Class)} */ @Deprecated
+    @SuppressWarnings("rawtypes")
     public static String getVerySimpleName(Class c) {
-        String s = c.getSimpleName();
-        if (s.indexOf('$')>=0)
-            s = s.substring(s.lastIndexOf('$')+1);
-        return s;
+        return JavaClassNames.verySimpleClassName(c);
     }
+    
+    /** @deprecated since 0.10.0 see {@link Boxing#PRIMITIVE_TO_BOXED} and its <code>inverse()</code> method */
+    @SuppressWarnings("rawtypes")
     public static final Map<Class,Class> BOXED_TO_UNBOXED_TYPES = ImmutableMap.<Class,Class>builder().
             put(Integer.class, Integer.TYPE).
             put(Long.class, Long.TYPE).
@@ -500,6 +217,8 @@ public class TypeCoercions {
             put(Character.class, Character.TYPE).
             put(Short.class, Short.TYPE).
             build();
+    /** @deprecated since 0.10.0 see {@link Boxing#PRIMITIVE_TO_BOXED} */ @Deprecated
+    @SuppressWarnings("rawtypes")
     public static final Map<Class,Class> UNBOXED_TO_BOXED_TYPES = ImmutableMap.<Class,Class>builder().
             put(Integer.TYPE, Integer.class).
             put(Long.TYPE, Long.class).
@@ -511,7 +230,10 @@ public class TypeCoercions {
             put(Short.TYPE, Short.class).
             build();
     
-    /** for automatic conversion */
+    /** for automatic conversion;
+     * @deprecated since 0.10.0 not used; there may be something similar in {@link Reflections} */ 
+    @Deprecated
+    @SuppressWarnings("rawtypes")
     public static Object getMatchingConstructor(Class target, Object ...arguments) {
         Constructor[] cc = target.getConstructors();
         for (Constructor c: cc) {
@@ -530,374 +252,4 @@ public class TypeCoercions {
         }
         return null;
     }
-
-    /** Registers an adapter for use with type coercion. Returns any old adapter. */
-    public synchronized static <A,B> Function registerAdapter(Class<A> sourceType, Class<B> targetType, Function<? super A,B> fn) {
-        return registry.put(targetType, sourceType, fn);
-    }
-
-    static { BrooklynInitialization.initTypeCoercionStandardAdapters(); }
-    
-    public static void initStandardAdapters() {
-        registerAdapter(CharSequence.class, String.class, new Function<CharSequence,String>() {
-            @Override
-            public String apply(CharSequence input) {
-                return input.toString();
-            }
-        });
-        registerAdapter(byte[].class, String.class, new Function<byte[],String>() {
-            @Override
-            public String apply(byte[] input) {
-                return new String(input);
-            }
-        });
-        registerAdapter(Collection.class, Set.class, new Function<Collection,Set>() {
-            @SuppressWarnings("unchecked")
-            @Override
-            public Set apply(Collection input) {
-                return Sets.newLinkedHashSet(input);
-            }
-        });
-        registerAdapter(Collection.class, List.class, new Function<Collection,List>() {
-            @SuppressWarnings("unchecked")
-            @Override
-            public List apply(Collection input) {
-                return Lists.newArrayList(input);
-            }
-        });
-        registerAdapter(String.class, InetAddress.class, new Function<String,InetAddress>() {
-            @Override
-            public InetAddress apply(String input) {
-                return Networking.getInetAddressWithFixedName(input);
-            }
-        });
-        registerAdapter(String.class, HostAndPort.class, new Function<String,HostAndPort>() {
-            @Override
-            public HostAndPort apply(String input) {
-                return HostAndPort.fromString(input);
-            }
-        });
-        registerAdapter(String.class, UserAndHostAndPort.class, new Function<String,UserAndHostAndPort>() {
-            @Override
-            public UserAndHostAndPort apply(String input) {
-                return UserAndHostAndPort.fromString(input);
-            }
-        });
-        registerAdapter(String.class, Cidr.class, new Function<String,Cidr>() {
-            @Override
-            public Cidr apply(String input) {
-                return new Cidr(input);
-            }
-        });
-        registerAdapter(String.class, URL.class, new Function<String,URL>() {
-            @Override
-            public URL apply(String input) {
-                try {
-                    return new URL(input);
-                } catch (Exception e) {
-                    throw Exceptions.propagate(e);
-                }
-            }
-        });
-        registerAdapter(URL.class, String.class, new Function<URL,String>() {
-            @Override
-            public String apply(URL input) {
-                return input.toString();
-            }
-        });
-        registerAdapter(String.class, URI.class, new Function<String,URI>() {
-            @Override
-            public URI apply(String input) {
-                return URI.create(input);
-            }
-        });
-        registerAdapter(URI.class, String.class, new Function<URI,String>() {
-            @Override
-            public String apply(URI input) {
-                return input.toString();
-            }
-        });
-        registerAdapter(Closure.class, ConfigurableEntityFactory.class, new Function<Closure,ConfigurableEntityFactory>() {
-            @SuppressWarnings("unchecked")
-            @Override
-            public ConfigurableEntityFactory apply(Closure input) {
-                return new ClosureEntityFactory(input);
-            }
-        });
-        @SuppressWarnings({"unused", "deprecation"})
-        Function<?,?> ignoredVarHereToAllowSuppressDeprecationWarning1 = registerAdapter(org.apache.brooklyn.core.entity.factory.EntityFactory.class, ConfigurableEntityFactory.class, new Function<org.apache.brooklyn.core.entity.factory.EntityFactory,ConfigurableEntityFactory>() {
-            @SuppressWarnings("unchecked")
-            @Override
-            public ConfigurableEntityFactory apply(org.apache.brooklyn.core.entity.factory.EntityFactory input) {
-                if (input instanceof ConfigurableEntityFactory) return (ConfigurableEntityFactory)input;
-                return new ConfigurableEntityFactoryFromEntityFactory(input);
-            }
-        });
-        @SuppressWarnings({"unused", "deprecation"})
-        Function<?,?> ignoredVarHereToAllowSuppressDeprecationWarning2 = registerAdapter(Closure.class, org.apache.brooklyn.core.entity.factory.EntityFactory.class, new Function<Closure,org.apache.brooklyn.core.entity.factory.EntityFactory>() {
-            @SuppressWarnings("unchecked")
-            @Override
-            public org.apache.brooklyn.core.entity.factory.EntityFactory apply(Closure input) {
-                return new ClosureEntityFactory(input);
-            }
-        });
-        registerAdapter(Closure.class, Predicate.class, new Function<Closure,Predicate>() {
-            @Override
-            public Predicate<?> apply(final Closure closure) {
-                return new Predicate<Object>() {
-                    @Override public boolean apply(Object input) {
-                        return (Boolean) closure.call(input);
-                    }
-                };
-            }
-        });
-        registerAdapter(Closure.class, Function.class, new Function<Closure,Function>() {
-            @Override
-            public Function apply(final Closure closure) {
-                return new Function() {
-                    @Override public Object apply(Object input) {
-                        return closure.call(input);
-                    }
-                };
-            }
-        });
-        registerAdapter(Object.class, Duration.class, new Function<Object,Duration>() {
-            @Override
-            public Duration apply(final Object input) {
-                return org.apache.brooklyn.util.time.Duration.of(input);
-            }
-        });
-        registerAdapter(Object.class, TimeDuration.class, new Function<Object,TimeDuration>() {
-            @SuppressWarnings("deprecation")
-            @Override
-            public TimeDuration apply(final Object input) {
-                log.warn("deprecated automatic coercion of Object to TimeDuration (set breakpoint in TypeCoercions to inspect, convert to Duration)");
-                return JavaGroovyEquivalents.toTimeDuration(input);
-            }
-        });
-        registerAdapter(TimeDuration.class, Long.class, new Function<TimeDuration,Long>() {
-            @Override
-            public Long apply(final TimeDuration input) {
-                log.warn("deprecated automatic coercion of TimeDuration to Long (set breakpoint in TypeCoercions to inspect, use Duration instead of Long!)");
-                return input.toMilliseconds();
-            }
-        });
-        registerAdapter(Integer.class, AtomicLong.class, new Function<Integer,AtomicLong>() {
-            @Override public AtomicLong apply(final Integer input) {
-                return new AtomicLong(input);
-            }
-        });
-        registerAdapter(Long.class, AtomicLong.class, new Function<Long,AtomicLong>() {
-            @Override public AtomicLong apply(final Long input) {
-                return new AtomicLong(input);
-            }
-        });
-        registerAdapter(String.class, AtomicLong.class, new Function<String,AtomicLong>() {
-            @Override public AtomicLong apply(final String input) {
-                return new AtomicLong(Long.parseLong(input.trim()));
-            }
-        });
-        registerAdapter(Integer.class, AtomicInteger.class, new Function<Integer,AtomicInteger>() {
-            @Override public AtomicInteger apply(final Integer input) {
-                return new AtomicInteger(input);
-            }
-        });
-        registerAdapter(String.class, AtomicInteger.class, new Function<String,AtomicInteger>() {
-            @Override public AtomicInteger apply(final String input) {
-                return new AtomicInteger(Integer.parseInt(input.trim()));
-            }
-        });
-        /** This always returns a {@link Double}, cast as a {@link Number}; 
-         * however primitives and boxers get exact typing due to call in #stringToPrimitive */
-        registerAdapter(String.class, Number.class, new Function<String,Number>() {
-            @Override
-            public Number apply(String input) {
-                return Double.valueOf(input);
-            }
-        });
-        registerAdapter(BigDecimal.class, Double.class, new Function<BigDecimal,Double>() {
-            @Override
-            public Double apply(BigDecimal input) {
-                return input.doubleValue();
-            }
-        });
-        registerAdapter(BigInteger.class, Long.class, new Function<BigInteger,Long>() {
-            @Override
-            public Long apply(BigInteger input) {
-                return input.longValue();
-            }
-        });
-        registerAdapter(BigInteger.class, Integer.class, new Function<BigInteger,Integer>() {
-            @Override
-            public Integer apply(BigInteger input) {
-                return input.intValue();
-            }
-        });
-        registerAdapter(String.class, BigDecimal.class, new Function<String,BigDecimal>() {
-            @Override
-            public BigDecimal apply(String input) {
-                return new BigDecimal(input);
-            }
-        });
-        registerAdapter(Double.class, BigDecimal.class, new Function<Double,BigDecimal>() {
-            @Override
-            public BigDecimal apply(Double input) {
-                return BigDecimal.valueOf(input);
-            }
-        });
-        registerAdapter(String.class, BigInteger.class, new Function<String,BigInteger>() {
-            @Override
-            public BigInteger apply(String input) {
-                return new BigInteger(input);
-            }
-        });
-        registerAdapter(Long.class, BigInteger.class, new Function<Long,BigInteger>() {
-            @Override
-            public BigInteger apply(Long input) {
-                return BigInteger.valueOf(input);
-            }
-        });
-        registerAdapter(Integer.class, BigInteger.class, new Function<Integer,BigInteger>() {
-            @Override
-            public BigInteger apply(Integer input) {
-                return BigInteger.valueOf(input);
-            }
-        });
-        registerAdapter(String.class, Date.class, new Function<String,Date>() {
-            @Override
-            public Date apply(final String input) {
-                return Time.parseDate(input);
-            }
-        });
-        registerAdapter(String.class, Class.class, new Function<String,Class>() {
-            @Override
-            public Class apply(final String input) {
-                try {
-                    return Class.forName(input);
-                } catch (ClassNotFoundException e) {
-                    throw Exceptions.propagate(e);
-                }
-            }
-        });
-        registerAdapter(String.class, AttributeSensor.class, new Function<String,AttributeSensor>() {
-            @Override
-            public AttributeSensor apply(final String input) {
-                Entity entity = BrooklynTaskTags.getContextEntity(Tasks.current());
-                if (entity!=null) {
-                    Sensor<?> result = entity.getEntityType().getSensor(input);
-                    if (result instanceof AttributeSensor) 
-                        return (AttributeSensor) result;
-                }
-                return Sensors.newSensor(Object.class, input);
-            }
-        });
-        registerAdapter(String.class, Sensor.class, new Function<String,Sensor>() {
-            @Override
-            public AttributeSensor apply(final String input) {
-                Entity entity = BrooklynTaskTags.getContextEntity(Tasks.current());
-                if (entity!=null) {
-                    Sensor<?> result = entity.getEntityType().getSensor(input);
-                    if (result != null) 
-                        return (AttributeSensor) result;
-                }
-                return Sensors.newSensor(Object.class, input);
-            }
-        });
-        registerAdapter(String.class, List.class, new Function<String,List>() {
-            @Override
-            public List<String> apply(final String input) {
-                return JavaStringEscapes.unwrapJsonishListIfPossible(input);
-            }
-        });
-        registerAdapter(String.class, Set.class, new Function<String,Set>() {
-            @Override
-            public Set<String> apply(final String input) {
-                return MutableSet.copyOf(JavaStringEscapes.unwrapJsonishListIfPossible(input)).asUnmodifiable();
-            }
-        });
-        registerAdapter(String.class, QuorumCheck.class, new Function<String,QuorumCheck>() {
-            @Override
-            public QuorumCheck apply(final String input) {
-                return QuorumChecks.of(input);
-            }
-        });
-        registerAdapter(Iterable.class, String[].class, new Function<Iterable, String[]>() {
-            @Nullable
-            @Override
-            public String[] apply(@Nullable Iterable list) {
-                if (list == null) return null;
-                String[] result = new String[Iterables.size(list)];
-                int count = 0;
-                for (Object element : list) {
-                    result[count++] = coerce(element, String.class);
-                }
-                return result;
-            }
-        });
-        registerAdapter(Iterable.class, Integer[].class, new Function<Iterable, Integer[]>() {
-            @Nullable
-            @Override
-            public Integer[] apply(@Nullable Iterable list) {
-                if (list == null) return null;
-                Integer[] result = new Integer[Iterables.size(list)];
-                int count = 0;
-                for (Object element : list) {
-                    result[count++] = coerce(element, Integer.class);
-                }
-                return result;
-            }
-        });
-        registerAdapter(Iterable.class, int[].class, new Function<Iterable, int[]>() {
-            @Nullable
-            @Override
-            public int[] apply(@Nullable Iterable list) {
-                if (list == null) return null;
-                int[] result = new int[Iterables.size(list)];
-                int count = 0;
-                for (Object element : list) {
-                    result[count++] = coerce(element, int.class);
-                }
-                return result;
-            }
-        });
-        registerAdapter(String.class, Map.class, new Function<String,Map>() {
-            @Override
-            public Map apply(final String input) {
-                Exception error = null;
-                
-                // first try wrapping in braces if needed
-                if (!input.trim().startsWith("{")) {
-                    try {
-                        return apply("{ "+input+" }");
-                    } catch (Exception e) {
-                        Exceptions.propagateIfFatal(e);
-                        // prefer this error
-                        error = e;
-                        // fall back to parsing without braces, e.g. if it's multiline
-                    }
-                }
-
-                try {
-                    return Yamls.getAs( Yamls.parseAll(input), Map.class );
-                } catch (Exception e) {
-                    Exceptions.propagateIfFatal(e);
-                    if (error!=null && input.indexOf('\n')==-1) {
-                        // prefer the original error if it wasn't braced and wasn't multiline
-                        e = error;
-                    }
-                    throw new IllegalArgumentException("Cannot parse string as map with flexible YAML parsing; "+
-                        (e instanceof ClassCastException ? "yaml treats it as a string" : 
-                        (e instanceof IllegalArgumentException && Strings.isNonEmpty(e.getMessage())) ? e.getMessage() :
-                        ""+e) );
-                }
-
-                // NB: previously we supported this also, when we did json above;
-                // yaml support is better as it supports quotes (and better than json because it allows dropping quotes)
-                // snake-yaml, our parser, also accepts key=value -- although i'm not sure this is strictly yaml compliant;
-                // our tests will catch it if snake behaviour changes, and we can reinstate this
-                // (but note it doesn't do quotes; see http://code.google.com/p/guava-libraries/issues/detail?id=412 for that):
-//                return ImmutableMap.copyOf(Splitter.on(",").trimResults().omitEmptyStrings().withKeyValueSeparator("=").split(input));
-            }
-        });
-    }
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/ec4da197/core/src/test/java/org/apache/brooklyn/util/core/internal/TypeCoercionsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/util/core/internal/TypeCoercionsTest.java b/core/src/test/java/org/apache/brooklyn/util/core/internal/TypeCoercionsTest.java
index 47d4c49..36a9ea7 100644
--- a/core/src/test/java/org/apache/brooklyn/util/core/internal/TypeCoercionsTest.java
+++ b/core/src/test/java/org/apache/brooklyn/util/core/internal/TypeCoercionsTest.java
@@ -33,9 +33,10 @@ import java.util.Map;
 import java.util.Set;
 
 import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
+import org.apache.brooklyn.test.Asserts;
 import org.apache.brooklyn.util.collections.MutableSet;
-import org.apache.brooklyn.util.core.flags.ClassCoercionException;
 import org.apache.brooklyn.util.core.flags.TypeCoercions;
+import org.apache.brooklyn.util.javalang.coerce.ClassCoercionException;
 import org.apache.brooklyn.util.text.StringPredicates;
 import org.codehaus.groovy.runtime.GStringImpl;
 import org.slf4j.Logger;
@@ -296,11 +297,11 @@ public class TypeCoercionsTest {
         Assert.assertEquals(s, ImmutableMap.of("a", "1", "b", "2"));
     }
 
-    @Test(expectedExceptions=IllegalArgumentException.class)
+    @Test(expectedExceptions=ClassCoercionException.class)
     public void testJsonStringWithoutBracesOrSpaceDisallowedAsMapCoercion() {
         // yaml requires spaces after the colon
-        Map<?,?> s = TypeCoercions.coerce("a:1,b:2", Map.class);
-        Assert.assertEquals(s, ImmutableMap.of("a", 1, "b", 2));
+        TypeCoercions.coerce("a:1,b:2", Map.class);
+        Asserts.shouldHaveFailedPreviously();
     }
     
     @Test
@@ -351,7 +352,7 @@ public class TypeCoercionsTest {
         assertEquals(TypeCoercions.coerce("1.0", Number.class), (Number) Double.valueOf(1.0));
     }
 
-    @Test(expectedExceptions = ClassCoercionException.class)
+    @Test(expectedExceptions = org.apache.brooklyn.util.javalang.coerce.ClassCoercionException.class)
     public void testInvalidCoercionThrowsClassCoercionException() {
         TypeCoercions.coerce(new Object(), TypeToken.of(Integer.class));
     }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/ec4da197/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/DefaultExceptionMapper.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/DefaultExceptionMapper.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/DefaultExceptionMapper.java
index dffc143..010bcca 100644
--- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/DefaultExceptionMapper.java
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/DefaultExceptionMapper.java
@@ -31,9 +31,9 @@ import org.apache.brooklyn.core.mgmt.entitlement.Entitlements;
 import org.apache.brooklyn.rest.domain.ApiError;
 import org.apache.brooklyn.rest.domain.ApiError.Builder;
 import org.apache.brooklyn.util.collections.MutableSet;
-import org.apache.brooklyn.util.core.flags.ClassCoercionException;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.exceptions.UserFacingException;
+import org.apache.brooklyn.util.javalang.coerce.ClassCoercionException;
 import org.apache.brooklyn.util.text.Strings;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/ec4da197/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/ClassCoercionException.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/ClassCoercionException.java b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/ClassCoercionException.java
new file mode 100644
index 0000000..fbc1cbe
--- /dev/null
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/ClassCoercionException.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.brooklyn.util.javalang.coerce;
+
+/**
+ * Thrown to indicate that {@link TypeCoercions} could not cast an object from one
+ * class to another.
+ */
+public class ClassCoercionException extends ClassCastException {
+    private static final long serialVersionUID = -4616045237993172497L;
+
+    private final Throwable cause;
+    
+    public ClassCoercionException() {
+        super();
+        cause = null;
+    }
+    public ClassCoercionException(String s) {
+        super(s);
+        cause = null;
+    }
+    public ClassCoercionException(String s, Throwable cause) {
+        super(s);
+        this.cause = cause;
+    }
+
+    @Override
+    public Throwable getCause() {
+        return cause;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/ec4da197/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/CoerceFunctionals.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/CoerceFunctionals.java b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/CoerceFunctionals.java
new file mode 100644
index 0000000..b03a8b3
--- /dev/null
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/CoerceFunctionals.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.brooklyn.util.javalang.coerce;
+
+import com.google.common.base.Function;
+
+public class CoerceFunctionals {
+
+    private CoerceFunctionals() {}
+    
+    public static class CoerceFunction<T> implements Function<Object, T> { 
+        private final TypeCoercer coercer;
+        private final Class<T> type;
+
+        public CoerceFunction(TypeCoercer coercer, Class<T> type) {
+            this.coercer = coercer;
+            this.type = type;
+        }
+        @Override
+        public T apply(Object input) {
+            return coercer.coerce(input, type);
+        }
+    }
+    
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/ec4da197/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/CommonAdaptorTypeCoercions.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/CommonAdaptorTypeCoercions.java b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/CommonAdaptorTypeCoercions.java
new file mode 100644
index 0000000..94c93e9
--- /dev/null
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/CommonAdaptorTypeCoercions.java
@@ -0,0 +1,380 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.brooklyn.util.javalang.coerce;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.net.URI;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.annotation.Nullable;
+
+import org.apache.brooklyn.util.collections.MutableSet;
+import org.apache.brooklyn.util.collections.QuorumCheck;
+import org.apache.brooklyn.util.collections.QuorumCheck.QuorumChecks;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.net.Cidr;
+import org.apache.brooklyn.util.net.Networking;
+import org.apache.brooklyn.util.net.UserAndHostAndPort;
+import org.apache.brooklyn.util.text.Strings;
+import org.apache.brooklyn.util.text.StringEscapes.JavaStringEscapes;
+import org.apache.brooklyn.util.time.Duration;
+import org.apache.brooklyn.util.time.Time;
+import org.apache.brooklyn.util.yaml.Yamls;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.google.common.net.HostAndPort;
+
+public class CommonAdaptorTypeCoercions {
+
+    private final TypeCoercerExtensible coercer;
+
+    public CommonAdaptorTypeCoercions(TypeCoercerExtensible coercer) {
+        this.coercer = coercer;
+    }
+
+    public TypeCoercerExtensible getCoercer() {
+        return coercer;
+    }
+    
+    public CommonAdaptorTypeCoercions registerAllAdapters() {
+        registerStandardAdapters();
+        registerRecursiveIterableAdapters();
+        registerClassForNameAdapters();
+        registerCollectionJsonAdapters();
+        
+        return this;
+    }
+    
+    /** Registers an adapter for use with type coercion. Returns any old adapter registered for this pair. */
+    public synchronized <A,B> Function<? super A,B> registerAdapter(Class<A> sourceType, Class<B> targetType, Function<? super A,B> fn) {
+        return coercer.registerAdapter(sourceType, targetType, fn);
+    }
+    
+    @SuppressWarnings("rawtypes")
+    public void registerStandardAdapters() {
+        registerAdapter(CharSequence.class, String.class, new Function<CharSequence,String>() {
+            @Override
+            public String apply(CharSequence input) {
+                return input.toString();
+            }
+        });
+        registerAdapter(byte[].class, String.class, new Function<byte[],String>() {
+            @Override
+            public String apply(byte[] input) {
+                return new String(input);
+            }
+        });
+        registerAdapter(Collection.class, Set.class, new Function<Collection,Set>() {
+            @SuppressWarnings("unchecked")
+            @Override
+            public Set apply(Collection input) {
+                return Sets.newLinkedHashSet(input);
+            }
+        });
+        registerAdapter(Collection.class, List.class, new Function<Collection,List>() {
+            @SuppressWarnings("unchecked")
+            @Override
+            public List apply(Collection input) {
+                return Lists.newArrayList(input);
+            }
+        });
+        registerAdapter(String.class, InetAddress.class, new Function<String,InetAddress>() {
+            @Override
+            public InetAddress apply(String input) {
+                return Networking.getInetAddressWithFixedName(input);
+            }
+        });
+        registerAdapter(String.class, HostAndPort.class, new Function<String,HostAndPort>() {
+            @Override
+            public HostAndPort apply(String input) {
+                return HostAndPort.fromString(input);
+            }
+        });
+        registerAdapter(String.class, UserAndHostAndPort.class, new Function<String,UserAndHostAndPort>() {
+            @Override
+            public UserAndHostAndPort apply(String input) {
+                return UserAndHostAndPort.fromString(input);
+            }
+        });
+        registerAdapter(String.class, Cidr.class, new Function<String,Cidr>() {
+            @Override
+            public Cidr apply(String input) {
+                return new Cidr(input);
+            }
+        });
+        registerAdapter(String.class, URL.class, new Function<String,URL>() {
+            @Override
+            public URL apply(String input) {
+                try {
+                    return new URL(input);
+                } catch (Exception e) {
+                    throw Exceptions.propagate(e);
+                }
+            }
+        });
+        registerAdapter(URL.class, String.class, new Function<URL,String>() {
+            @Override
+            public String apply(URL input) {
+                return input.toString();
+            }
+        });
+        registerAdapter(String.class, URI.class, new Function<String,URI>() {
+            @Override
+            public URI apply(String input) {
+                return URI.create(input);
+            }
+        });
+        registerAdapter(URI.class, String.class, new Function<URI,String>() {
+            @Override
+            public String apply(URI input) {
+                return input.toString();
+            }
+        });
+        registerAdapter(Object.class, Duration.class, new Function<Object,Duration>() {
+            @Override
+            public Duration apply(final Object input) {
+                return org.apache.brooklyn.util.time.Duration.of(input);
+            }
+        });
+
+        registerAdapter(Integer.class, AtomicLong.class, new Function<Integer,AtomicLong>() {
+            @Override public AtomicLong apply(final Integer input) {
+                return new AtomicLong(input);
+            }
+        });
+        registerAdapter(Long.class, AtomicLong.class, new Function<Long,AtomicLong>() {
+            @Override public AtomicLong apply(final Long input) {
+                return new AtomicLong(input);
+            }
+        });
+        registerAdapter(String.class, AtomicLong.class, new Function<String,AtomicLong>() {
+            @Override public AtomicLong apply(final String input) {
+                return new AtomicLong(Long.parseLong(input.trim()));
+            }
+        });
+        registerAdapter(Integer.class, AtomicInteger.class, new Function<Integer,AtomicInteger>() {
+            @Override public AtomicInteger apply(final Integer input) {
+                return new AtomicInteger(input);
+            }
+        });
+        registerAdapter(String.class, AtomicInteger.class, new Function<String,AtomicInteger>() {
+            @Override public AtomicInteger apply(final String input) {
+                return new AtomicInteger(Integer.parseInt(input.trim()));
+            }
+        });
+        /** This always returns a {@link Double}, cast as a {@link Number}; 
+         * however primitives and boxers get exact typing due to call in #stringToPrimitive */
+        registerAdapter(String.class, Number.class, new Function<String,Number>() {
+            @Override
+            public Number apply(String input) {
+                return Double.valueOf(input);
+            }
+        });
+        registerAdapter(BigDecimal.class, Double.class, new Function<BigDecimal,Double>() {
+            @Override
+            public Double apply(BigDecimal input) {
+                return input.doubleValue();
+            }
+        });
+        registerAdapter(BigInteger.class, Long.class, new Function<BigInteger,Long>() {
+            @Override
+            public Long apply(BigInteger input) {
+                return input.longValue();
+            }
+        });
+        registerAdapter(BigInteger.class, Integer.class, new Function<BigInteger,Integer>() {
+            @Override
+            public Integer apply(BigInteger input) {
+                return input.intValue();
+            }
+        });
+        registerAdapter(String.class, BigDecimal.class, new Function<String,BigDecimal>() {
+            @Override
+            public BigDecimal apply(String input) {
+                return new BigDecimal(input);
+            }
+        });
+        registerAdapter(Double.class, BigDecimal.class, new Function<Double,BigDecimal>() {
+            @Override
+            public BigDecimal apply(Double input) {
+                return BigDecimal.valueOf(input);
+            }
+        });
+        registerAdapter(String.class, BigInteger.class, new Function<String,BigInteger>() {
+            @Override
+            public BigInteger apply(String input) {
+                return new BigInteger(input);
+            }
+        });
+        registerAdapter(Long.class, BigInteger.class, new Function<Long,BigInteger>() {
+            @Override
+            public BigInteger apply(Long input) {
+                return BigInteger.valueOf(input);
+            }
+        });
+        registerAdapter(Integer.class, BigInteger.class, new Function<Integer,BigInteger>() {
+            @Override
+            public BigInteger apply(Integer input) {
+                return BigInteger.valueOf(input);
+            }
+        });
+        registerAdapter(String.class, Date.class, new Function<String,Date>() {
+            @Override
+            public Date apply(final String input) {
+                return Time.parseDate(input);
+            }
+        });
+        registerAdapter(String.class, QuorumCheck.class, new Function<String,QuorumCheck>() {
+            @Override
+            public QuorumCheck apply(final String input) {
+                return QuorumChecks.of(input);
+            }
+        });
+    }
+    
+    @SuppressWarnings("rawtypes")
+    public void registerRecursiveIterableAdapters() {
+        
+        // these refer to the coercer to recursively coerce;
+        // they throw if there are errors (but the registry apply loop will catch and handle),
+        // as currently the registry does not support Maybe or opting-out
+        
+        registerAdapter(Iterable.class, String[].class, new Function<Iterable, String[]>() {
+            @Nullable
+            @Override
+            public String[] apply(@Nullable Iterable list) {
+                if (list == null) return null;
+                String[] result = new String[Iterables.size(list)];
+                int count = 0;
+                for (Object element : list) {
+                    result[count++] = coercer.coerce(element, String.class);
+                }
+                return result;
+            }
+        });
+        registerAdapter(Iterable.class, Integer[].class, new Function<Iterable, Integer[]>() {
+            @Nullable
+            @Override
+            public Integer[] apply(@Nullable Iterable list) {
+                if (list == null) return null;
+                Integer[] result = new Integer[Iterables.size(list)];
+                int count = 0;
+                for (Object element : list) {
+                    result[count++] = coercer.coerce(element, Integer.class);
+                }
+                return result;
+            }
+        });
+        registerAdapter(Iterable.class, int[].class, new Function<Iterable, int[]>() {
+            @Nullable
+            @Override
+            public int[] apply(@Nullable Iterable list) {
+                if (list == null) return null;
+                int[] result = new int[Iterables.size(list)];
+                int count = 0;
+                for (Object element : list) {
+                    result[count++] = coercer.coerce(element, int.class);
+                }
+                return result;
+            }
+        });
+    }
+    
+    @SuppressWarnings("rawtypes")
+    public void registerClassForNameAdapters() {
+        registerAdapter(String.class, Class.class, new Function<String,Class>() {
+            @Override
+            public Class apply(final String input) {
+                try {
+                    return Class.forName(input);
+                } catch (ClassNotFoundException e) {
+                    throw Exceptions.propagate(e);
+                }
+            }
+        });        
+    }
+    
+    @SuppressWarnings("rawtypes")
+    public void registerCollectionJsonAdapters() {
+        registerAdapter(String.class, List.class, new Function<String,List>() {
+            @Override
+            public List<String> apply(final String input) {
+                return JavaStringEscapes.unwrapJsonishListIfPossible(input);
+            }
+        });
+        registerAdapter(String.class, Set.class, new Function<String,Set>() {
+            @Override
+            public Set<String> apply(final String input) {
+                return MutableSet.copyOf(JavaStringEscapes.unwrapJsonishListIfPossible(input)).asUnmodifiable();
+            }
+        });
+        registerAdapter(String.class, Map.class, new Function<String,Map>() {
+            @Override
+            public Map apply(final String input) {
+                Exception error = null;
+                
+                // first try wrapping in braces if needed
+                if (!input.trim().startsWith("{")) {
+                    try {
+                        return apply("{ "+input+" }");
+                    } catch (Exception e) {
+                        Exceptions.propagateIfFatal(e);
+                        // prefer this error
+                        error = e;
+                        // fall back to parsing without braces, e.g. if it's multiline
+                    }
+                }
+
+                try {
+                    return Yamls.getAs( Yamls.parseAll(input), Map.class );
+                } catch (Exception e) {
+                    Exceptions.propagateIfFatal(e);
+                    if (error!=null && input.indexOf('\n')==-1) {
+                        // prefer the original error if it wasn't braced and wasn't multiline
+                        e = error;
+                    }
+                    throw new IllegalArgumentException("Cannot parse string as map with flexible YAML parsing; "+
+                        (e instanceof ClassCastException ? "yaml treats it as a string" : 
+                        (e instanceof IllegalArgumentException && Strings.isNonEmpty(e.getMessage())) ? e.getMessage() :
+                        ""+e) );
+                }
+
+                // NB: previously we supported this also, when we did json above;
+                // yaml support is better as it supports quotes (and better than json because it allows dropping quotes)
+                // snake-yaml, our parser, also accepts key=value -- although i'm not sure this is strictly yaml compliant;
+                // our tests will catch it if snake behaviour changes, and we can reinstate this
+                // (but note it doesn't do quotes; see http://code.google.com/p/guava-libraries/issues/detail?id=412 for that):
+//                return ImmutableMap.copyOf(Splitter.on(",").trimResults().omitEmptyStrings().withKeyValueSeparator("=").split(input));
+            }
+        });
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/ec4da197/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/EnumTypeCoercions.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/EnumTypeCoercions.java b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/EnumTypeCoercions.java
new file mode 100644
index 0000000..b57625a
--- /dev/null
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/EnumTypeCoercions.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.brooklyn.util.javalang.coerce;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import org.apache.brooklyn.util.guava.Functionals;
+import org.apache.brooklyn.util.guava.Maybe;
+import org.apache.brooklyn.util.javalang.Enums;
+import org.apache.brooklyn.util.javalang.JavaClassNames;
+
+import com.google.common.base.CaseFormat;
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+
+public class EnumTypeCoercions {
+
+    /**
+     * Type coercion {@link Function function} for {@link Enum enums}.
+     * <p>
+     * Tries to convert the string to {@link CaseFormat#UPPER_UNDERSCORE} first,
+     * handling all of the different {@link CaseFormat format} possibilites. Failing 
+     * that, it tries a case-insensitive comparison with the valid enum values.
+     * <p>
+     * Returns {@code defaultValue} if the string cannot be converted.
+     *
+     * @see TypeCoercions#coerce(Object, Class)
+     * @see Enum#valueOf(Class, String)
+     */
+    public static <E extends Enum<E>> Function<String, E> stringToEnum(final Class<E> type, @Nullable final E defaultValue) {
+        return new StringToEnumFunction<E>(type, defaultValue);
+    }
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public static <T> Function<String, T> stringToEnumUntyped(final Class<? super T> type, @Nullable final T defaultValue) {
+        if (!type.isEnum()) return new Functionals.ConstantFunction<String,T>(null);
+        return (Function<String, T>) new StringToEnumFunction((Class<Enum>)type, (Enum)defaultValue);
+    }
+    
+    private static class StringToEnumFunction<E extends Enum<E>> implements Function<String, E> {
+        private final Class<E> type;
+        private final E defaultValue;
+        
+        public StringToEnumFunction(Class<E> type, @Nullable E defaultValue) {
+            this.type = type;
+            this.defaultValue = defaultValue;
+        }
+        @Override
+        public E apply(String input) {
+            return tryCoerce(input, type).or(defaultValue);
+        }
+    }
+    
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public static <T> Maybe<T> tryCoerceUntyped(String input, Class<T> targetType) {
+        if (input==null) return null;
+        if (targetType==null) return Maybe.absent("Null enum type");
+        if (!targetType.isEnum()) return Maybe.absent("Type '"+targetType+"' is not an enum");
+        return tryCoerce(input, (Class<Enum>)targetType);
+    }
+    
+    public static <E extends Enum<E>> Maybe<E> tryCoerce(String input, Class<E> targetType) {
+        if (input==null) return null;
+        if (targetType==null) return Maybe.absent("Null enum type");
+        if (!targetType.isEnum()) return Maybe.absent("Type '"+targetType+"' is not an enum");
+        
+        List<String> options = ImmutableList.of(
+                input,
+                CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_UNDERSCORE, input),
+                CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_UNDERSCORE, input),
+                CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, input),
+                CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, input));
+        for (String value : options) {
+            try {
+                return Maybe.of(Enum.valueOf(targetType, value));
+            } catch (IllegalArgumentException iae) {
+                continue;
+            }
+        }
+        Maybe<E> result = Enums.valueOfIgnoreCase(targetType, input);
+        if (result.isPresent()) return result;
+        return Maybe.absent(new ClassCoercionException("Invalid value '"+input+"' for "+JavaClassNames.simpleClassName(targetType)+"; expected one of "+
+            Arrays.asList(Enums.values(targetType))));
+    }
+    
+}


Mime
View raw message