/* * * 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.qpid.server.model; import java.io.IOException; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import org.codehaus.jackson.map.ObjectMapper; import org.apache.qpid.server.util.ServerScopedRuntimeException; abstract class AttributeValueConverter { static final AttributeValueConverter STRING_CONVERTER = new AttributeValueConverter() { @Override public String convert(final Object value, final ConfiguredObject object) { return value == null ? null : AbstractConfiguredObject.interpolate(object, value.toString()); } }; static final AttributeValueConverter UUID_CONVERTER = new AttributeValueConverter() { @Override public UUID convert(final Object value, final ConfiguredObject object) { if(value instanceof UUID) { return (UUID) value; } else if(value instanceof String) { return UUID.fromString(AbstractConfiguredObject.interpolate(object, (String) value)); } else if(value == null) { return null; } else { throw new IllegalArgumentException("Cannot convert type " + value.getClass() + " to a UUID"); } } }; static final AttributeValueConverter LONG_CONVERTER = new AttributeValueConverter() { @Override public Long convert(final Object value, final ConfiguredObject object) { if(value instanceof Long) { return (Long) value; } else if(value instanceof Number) { return ((Number) value).longValue(); } else if(value instanceof String) { String interpolated = AbstractConfiguredObject.interpolate(object, (String) value); try { return Long.valueOf(interpolated); } catch(NumberFormatException e) { throw new IllegalArgumentException("Cannot convert string '" + interpolated + "'",e); } } else if(value == null) { return null; } else { throw new IllegalArgumentException("Cannot convert type " + value.getClass() + " to a Long"); } } }; static final AttributeValueConverter INT_CONVERTER = new AttributeValueConverter() { @Override public Integer convert(final Object value, final ConfiguredObject object) { if(value instanceof Integer) { return (Integer) value; } else if(value instanceof Number) { return ((Number) value).intValue(); } else if(value instanceof String) { String interpolated = AbstractConfiguredObject.interpolate(object, (String) value); try { return Integer.valueOf(interpolated); } catch(NumberFormatException e) { throw new IllegalArgumentException("Cannot convert string '" + interpolated + "'",e); } } else if(value == null) { return null; } else { throw new IllegalArgumentException("Cannot convert type " + value.getClass() + " to an Integer"); } } }; static final AttributeValueConverter SHORT_CONVERTER = new AttributeValueConverter() { @Override public Short convert(final Object value, final ConfiguredObject object) { if(value instanceof Short) { return (Short) value; } else if(value instanceof Number) { return ((Number) value).shortValue(); } else if(value instanceof String) { String interpolated = AbstractConfiguredObject.interpolate(object, (String) value); try { return Short.valueOf(interpolated); } catch(NumberFormatException e) { throw new IllegalArgumentException("Cannot convert string '" + interpolated + "'",e); } } else if(value == null) { return null; } else { throw new IllegalArgumentException("Cannot convert type " + value.getClass() + " to a Short"); } } }; static final AttributeValueConverter BOOLEAN_CONVERTER = new AttributeValueConverter() { @Override public Boolean convert(final Object value, final ConfiguredObject object) { if(value instanceof Boolean) { return (Boolean) value; } else if(value instanceof String) { return Boolean.valueOf(AbstractConfiguredObject.interpolate(object, (String) value)); } else if(value == null) { return null; } else { throw new IllegalArgumentException("Cannot convert type " + value.getClass() + " to a Boolean"); } } }; static final AttributeValueConverter LIST_CONVERTER = new AttributeValueConverter() { @Override public List convert(final Object value, final ConfiguredObject object) { if(value instanceof List) { return Collections.unmodifiableList((List) value); } else if(value instanceof Object[]) { return convert(Arrays.asList((Object[]) value),object); } else if(value instanceof String) { return Collections.unmodifiableList(convertFromJson((String) value, object, List.class)); } else if(value == null) { return null; } else { throw new IllegalArgumentException("Cannot convert type " + value.getClass() + " to a List"); } } }; static final AttributeValueConverter SET_CONVERTER = new AttributeValueConverter() { @Override public Set convert(final Object value, final ConfiguredObject object) { if(value instanceof Set) { return Collections.unmodifiableSet((Set) value); } else if(value instanceof Object[]) { return convert(new HashSet(Arrays.asList((Object[])value)),object); } else if(value instanceof String) { return Collections.unmodifiableSet(convertFromJson((String) value, object, Set.class)); } else if(value == null) { return null; } else { throw new IllegalArgumentException("Cannot convert type " + value.getClass() + " to a Set"); } } }; static final AttributeValueConverter COLLECTION_CONVERTER = new AttributeValueConverter() { @Override public Collection convert(final Object value, final ConfiguredObject object) { if(value instanceof Collection) { return Collections.unmodifiableCollection((Collection) value); } else if(value instanceof Object[]) { return convert(Arrays.asList((Object[]) value), object); } else if(value instanceof String) { return Collections.unmodifiableCollection(convertFromJson((String) value, object, Collection.class)); } else if(value == null) { return null; } else { throw new IllegalArgumentException("Cannot convert type " + value.getClass() + " to a Collection"); } } }; static final AttributeValueConverter MAP_CONVERTER = new AttributeValueConverter() { @Override public Map convert(final Object value, final ConfiguredObject object) { if(value instanceof Map) { Map originalMap = (Map) value; Map resolvedMap = new LinkedHashMap(originalMap.size()); for(Map.Entry entry : originalMap.entrySet()) { Object key = entry.getKey(); Object val = entry.getValue(); resolvedMap.put(key instanceof String ? AbstractConfiguredObject.interpolate(object, (String) key) : key, val instanceof String ? AbstractConfiguredObject.interpolate(object, (String) val) : val); } return Collections.unmodifiableMap(resolvedMap); } else if(value == null) { return null; } else if(value instanceof String) { return Collections.unmodifiableMap(convertFromJson((String) value, object, Map.class)); } else { throw new IllegalArgumentException("Cannot convert type " + value.getClass() + " to a Map"); } } }; private static T convertFromJson(final String value, final ConfiguredObject object, final Class valueType) { String interpolated = AbstractConfiguredObject.interpolate(object, value); ObjectMapper objectMapper = new ObjectMapper(); try { return objectMapper.readValue(interpolated, valueType); } catch (IOException e) { throw new IllegalArgumentException("Cannot convert String '" + value + "'" + (value.equals(interpolated) ? "" : (" (interpolated to '" + interpolated + "')")) + " to a " + valueType.getSimpleName()); } } static AttributeValueConverter getConverter(final Class type, final Type returnType) { if(type == String.class) { return (AttributeValueConverter) STRING_CONVERTER; } else if(type == Integer.class) { return (AttributeValueConverter) INT_CONVERTER; } else if(type == Short.class) { return (AttributeValueConverter) SHORT_CONVERTER; } else if(type == Long.class) { return (AttributeValueConverter) LONG_CONVERTER; } else if(type == Boolean.class) { return (AttributeValueConverter) BOOLEAN_CONVERTER; } else if(type == UUID.class) { return (AttributeValueConverter) UUID_CONVERTER; } else if(Enum.class.isAssignableFrom(type)) { return (AttributeValueConverter) new EnumConverter((Class)type); } else if(List.class.isAssignableFrom(type)) { if (returnType instanceof ParameterizedType) { Type parameterizedType = ((ParameterizedType) returnType).getActualTypeArguments()[0]; return (AttributeValueConverter) new GenericListConverter(parameterizedType); } else { return (AttributeValueConverter) LIST_CONVERTER; } } else if(Set.class.isAssignableFrom(type)) { if (returnType instanceof ParameterizedType) { Type parameterizedType = ((ParameterizedType) returnType).getActualTypeArguments()[0]; return (AttributeValueConverter) new GenericSetConverter(parameterizedType); } else { return (AttributeValueConverter) SET_CONVERTER; } } else if(Map.class.isAssignableFrom(type)) { return (AttributeValueConverter) MAP_CONVERTER; } else if(Collection.class.isAssignableFrom(type)) { if (returnType instanceof ParameterizedType) { Type parameterizedType = ((ParameterizedType) returnType).getActualTypeArguments()[0]; return (AttributeValueConverter) new GenericCollectionConverter(parameterizedType); } else { return (AttributeValueConverter) COLLECTION_CONVERTER; } } else if(ConfiguredObject.class.isAssignableFrom(type)) { return (AttributeValueConverter) new ConfiguredObjectConverter(type); } throw new IllegalArgumentException("Cannot create attribute converter of type " + type.getName()); } abstract T convert(Object value, final ConfiguredObject object); public static class GenericListConverter extends AttributeValueConverter { private final AttributeValueConverter _memberConverter; public GenericListConverter(final Type genericType) { _memberConverter = getConverter(getRawType(genericType), genericType); } @Override public List convert(final Object value, final ConfiguredObject object) { if(value instanceof Collection) { Collection original = (Collection)value; List converted = new ArrayList(original.size()); for(Object member : original) { converted.add(_memberConverter.convert(member, object)); } return Collections.unmodifiableList(converted); } else if(value instanceof Object[]) { return convert(Arrays.asList((Object[])value),object); } else if(value == null) { return null; } else { if(value instanceof String) { String interpolated = AbstractConfiguredObject.interpolate(object, (String) value); ObjectMapper objectMapper = new ObjectMapper(); try { return convert(objectMapper.readValue(interpolated, List.class), object); } catch (IOException e) { // fall through to the non-JSON single object case } } return Collections.unmodifiableList(Collections.singletonList(_memberConverter.convert(value, object))); } } } public static class GenericSetConverter extends AttributeValueConverter { private final AttributeValueConverter _memberConverter; public GenericSetConverter(final Type genericType) { _memberConverter = getConverter(getRawType(genericType), genericType); } @Override public Set convert(final Object value, final ConfiguredObject object) { if(value instanceof Collection) { Collection original = (Collection)value; Set converted = new HashSet(original.size()); for(Object member : original) { converted.add(_memberConverter.convert(member, object)); } return Collections.unmodifiableSet(converted); } else if(value instanceof Object[]) { return convert(new HashSet(Arrays.asList((Object[])value)),object); } else if(value == null) { return null; } else { if(value instanceof String) { String interpolated = AbstractConfiguredObject.interpolate(object, (String) value); ObjectMapper objectMapper = new ObjectMapper(); try { return convert(objectMapper.readValue(interpolated, Set.class), object); } catch (IOException e) { // fall through to the non-JSON single object case } } return Collections.unmodifiableSet(Collections.singleton(_memberConverter.convert(value, object))); } } } public static class GenericCollectionConverter extends AttributeValueConverter { private final AttributeValueConverter _memberConverter; public GenericCollectionConverter(final Type genericType) { _memberConverter = getConverter(getRawType(genericType), genericType); } @Override public Collection convert(final Object value, final ConfiguredObject object) { if(value instanceof Collection) { Collection original = (Collection)value; Collection converted = new ArrayList(original.size()); for(Object member : original) { converted.add(_memberConverter.convert(member, object)); } return Collections.unmodifiableCollection(converted); } else if(value instanceof Object[]) { return convert(Arrays.asList((Object[])value),object); } else if(value == null) { return null; } else { if(value instanceof String) { String interpolated = AbstractConfiguredObject.interpolate(object, (String) value); ObjectMapper objectMapper = new ObjectMapper(); try { return convert(objectMapper.readValue(interpolated, List.class), object); } catch (IOException e) { // fall through to the non-JSON single object case } } return Collections.unmodifiableCollection(Collections.singletonList(_memberConverter.convert(value, object))); } } } static final class EnumConverter> extends AttributeValueConverter { private final Class _klazz; private EnumConverter(final Class klazz) { _klazz = klazz; } @Override public X convert(final Object value, final ConfiguredObject object) { if(value == null) { return null; } else if(_klazz.isInstance(value)) { return (X) value; } else if(value instanceof String) { return Enum.valueOf(_klazz, AbstractConfiguredObject.interpolate(object, (String) value)); } else { throw new IllegalArgumentException("Cannot convert type " + value.getClass() + " to a " + _klazz.getName()); } } } static final class ConfiguredObjectConverter> extends AttributeValueConverter { private final Class _klazz; private ConfiguredObjectConverter(final Class klazz) { _klazz = klazz; } @Override public X convert(final Object value, final ConfiguredObject object) { if(value == null) { return null; } else if(_klazz.isInstance(value)) { return (X) value; } else if(value instanceof UUID) { Collection reachable = object.getModel().getReachableObjects(object, _klazz); for(X candidate : reachable) { if(candidate.getId().equals(value)) { return candidate; } } throw new UnknownConfiguredObjectException(_klazz, (UUID)value); } else if(value instanceof String) { String valueStr = AbstractConfiguredObject.interpolate(object, (String) value); Collection reachable = object.getModel().getReachableObjects(object, _klazz); for(X candidate : reachable) { if(candidate.getName().equals(valueStr)) { return candidate; } } try { UUID id = UUID.fromString(valueStr); return convert(id, object); } catch (IllegalArgumentException e) { throw new UnknownConfiguredObjectException(_klazz, valueStr); } } else { throw new IllegalArgumentException("Cannot convert type " + value.getClass() + " to a " + _klazz.getName()); } } } private static Class getRawType(Type t) { if(t instanceof Class) { return (Class)t; } else if(t instanceof ParameterizedType) { return (Class)((ParameterizedType)t).getRawType(); } else if(t instanceof TypeVariable) { Type[] bounds = ((TypeVariable)t).getBounds(); if(bounds.length == 1) { return getRawType(bounds[0]); } } throw new ServerScopedRuntimeException("Unable to process type when constructing configuration model: " + t); } }