Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  package org.skife.config;
  
  import  net.sf.cglib.proxy.Callback;
  import  net.sf.cglib.proxy.CallbackFilter;
  import  net.sf.cglib.proxy.Enhancer;
  import  net.sf.cglib.proxy.Factory;
  import  net.sf.cglib.proxy.MethodInterceptor;
  import  net.sf.cglib.proxy.MethodProxy;
  import  net.sf.cglib.proxy.NoOp;
 
 import java.util.List;
 import java.util.Map;
 
 {
     private static final Logger logger = LoggerFactory.getLogger(ConfigurationObjectFactory.class);
     private static final ConcurrentMap<Class<?>, Factory> factories = new ConcurrentHashMap<Class<?>, Factory>();
     private final ConfigSource config;
     private final Bully bully;
 
     public ConfigurationObjectFactory(Properties props)
     {
         this(new SimplePropertyConfigSource(props));
     }
 
     public ConfigurationObjectFactory(ConfigSource config)
     {
         this. = config;
         this. = new Bully();
     }
 
     public void addCoercible(final Coercible<?> coercible)
     {
         this..addCoercible(coercible);
     }
 
 
     public <T> T buildWithReplacements(Class<T> configClassMap<StringStringmappedReplacements)
     {
         return internalBuild(configClassmappedReplacements);
     }
 
     public <T> T build(Class<T> configClass)
     {
         return internalBuild(configClassnull);
     }
 
     private <T> T internalBuild(Class<T> configClassMap<StringStringmappedReplacements)
     {
         final List<Callback> callbacks = new ArrayList<Callback>();
         final Map<MethodIntegerslots = new HashMap<MethodInteger>();
         callbacks.add(NoOp.INSTANCE);
 
         int count = 1;
 
         // Hook up a toString method that prints out the settings for that bean if possible.
         final Method toStringMethod = findToStringMethod(configClass);
         if (toStringMethod != null) {
             slots.put(toStringMethodcount++);
             callbacks.add(new ConfigMagicBeanToString(callbacks));
         }
 
         // Now hook up the actual value interceptors.
         for (final Method method : configClass.getMethods()) {
             if (method.isAnnotationPresent(Config.class)) {
                 final Config annotation = method.getAnnotation(Config.class);
                 slots.put(methodcount++);
 
                 if (method.getParameterTypes().length > 0) {
                     if (mappedReplacements != null) {
                         throw new RuntimeException("Replacements are not supported for parameterized config methods");
                     }
                     buildParameterized(callbacksmethodannotation);
                 }
                 else {
                     buildSimple(callbacksmethodannotationmappedReplacementsnull);
                 }
             }
             else if (method.isAnnotationPresent(ConfigReplacements.class)) {
                 final ConfigReplacements annotation = method.getAnnotation(ConfigReplacements.class);
 
                 slots.put(methodcount++);
 
                 if (..equals(annotation.value())) {
                     Map<StringStringfixedMap = mappedReplacements == null ?
                             Collections.<StringString>emptyMap() : Collections.unmodifiableMap(mappedReplacements);
 
                     callbacks.add(new ConfigMagicFixedValue(method"annotation: @ConfigReplacements"fixedMapfalse));
                } else {
                    buildSimple(callbacksmethodnullmappedReplacementsannotation);
                }
            }
            else if (Modifier.isAbstract(method.getModifiers())) {
                throw new AbstractMethodError(String.format("Method [%s] is abstract and lacks an @Config annotation",
                                                            method.toGenericString()));
            }
        }
        if (.containsKey(configClass)) {
            Factory f = .get(configClass);
            return configClass.cast(f.newInstance(callbacks.toArray(new Callback[callbacks.size()])));
        }
        else {
            Enhancer e = new Enhancer();
            e.setSuperclass(configClass);
            e.setCallbackFilter(new ConfigMagicCallbackFilter(slots));
            e.setCallbacks(callbacks.toArray(new Callback[callbacks.size()]));
            T rt = configClass.cast(e.create());
            .putIfAbsent(configClass, (Factory) rt);
            return rt;
        }
    }
    private void buildSimple(List<Callback> callbacksMethod methodConfig annotation,
                             Map<StringStringmappedReplacementsConfigReplacements mapAnnotation)
    {
        String assignedFrom = null;
        String[] propertyNames = new String[0];
        String value = null;
        // Annotation will be null for an @ConfigReplacements, in which case "value" will
        // be preset and ready to be defaulted + bullied
        if (annotation != null) {
            propertyNames = annotation.value();
            if (propertyNames == null || propertyNames.length == 0) {
                throw new IllegalArgumentException("Method " +
                                                   method.toGenericString() +
                                                   " declares config annotation but no field name!");
            }
            for (String propertyName : propertyNames) {
                if (mappedReplacements != null) {
                    propertyName = applyReplacements(propertyNamemappedReplacements);
                }
                value = .getString(propertyName);
                // First value found wins
                if (value != null) {
                    assignedFrom = "property: '" + propertyName + "'";
                    .info("Assigning value [{}] for [{}] on [{}#{}()]",
                                new Object[] { valuepropertyNamemethod.getDeclaringClass().getName(), method.getName() });
                    break;
                }
            }
        } else {
            if (mapAnnotation == null) {
                throw new IllegalStateException("Neither @Config nor @ConfigReplacements provided, this should not be possible!");
            }
            String key = mapAnnotation.value();
            value = mappedReplacements == null ? null : mappedReplacements.get(key);
            if (value != null) {
                assignedFrom = "@ConfigReplacements: key '" + key + "'";
                .info("Assigning mappedReplacement value [{}] for [{}] on [{}#{}()]",
                            new Object[] { valuekeymethod.getDeclaringClass().getName(), method.getName() });
            }
        }
        final boolean hasDefault = method.isAnnotationPresent(Default.class);
        final boolean hasDefaultNull = method.isAnnotationPresent(DefaultNull.class);
        if (hasDefault && hasDefaultNull) {
            throw new IllegalArgumentException(String.format("@Default and @DefaultNull present in [%s]"method.toGenericString()));
        }
        boolean useMethod = false;
        //
        // This is how the value logic works if no value has been set by the config:
        //
        // - if the @Default annotation is present, use its value.
        // - if the @DefaultNull annotation is present, accept null as the value
        // - otherwise, check whether the method is not abstract. If it is not, mark the callback that it should call the method and
        //   ignore the passed in value (which will be null)
        // - if all else fails, throw an exception.
        //
        if (value == null) {
            if (hasDefault) {
                value = method.getAnnotation(Default.class).value();
                assignedFrom = "annotation: @Default";
                .info("Assigning default value [{}] for {} on [{}#{}()]",
                            new Object[] { valuepropertyNamesmethod.getDeclaringClass().getName(), method.getName() });
            }
            else if (hasDefaultNull) {
                .info("Assigning null default value for {} on [{}#{}()]",
                            new Object[] { propertyNamesmethod.getDeclaringClass().getName(), method.getName() });
                assignedFrom = "annotation: @DefaultNull";
            }
            else {
                // Final try: Is the method is actually callable?
                if (!Modifier.isAbstract(method.getModifiers())) {
                    useMethod = true;
                    assignedFrom = "method: '" + method.getName() + "()'";
                    .info("Using method itself for {} on [{}#{}()]",
                                new Object[] { propertyNamesmethod.getDeclaringClass().getName(), method.getName() });
                }
                else {
                    throw new IllegalArgumentException(String.format("No value present for '%s' in [%s]",
                            prettyPrint(propertyNamesmappedReplacements),
                            method.toGenericString()));
                }
            }
        }
        final Object finalValue = .coerce(method.getGenericReturnType(), valuemethod.getAnnotation(Separator.class));
        callbacks.add(new ConfigMagicFixedValue(methodassignedFromfinalValueuseMethod));
    }
    private String applyReplacements(String propertyNameMap<StringStringmappedReplacements)
    {
        for (String key : mappedReplacements.keySet()) {
            String token = makeToken(key);
            String replacement = mappedReplacements.get(key);
            propertyName = propertyName.replace(tokenreplacement);
        }
        return propertyName;
    }
    private void buildParameterized(List<Callback> callbacksMethod methodConfig annotation)
    {
        String defaultValue = null;
        final boolean hasDefault = method.isAnnotationPresent(Default.class);
        final boolean hasDefaultNull = method.isAnnotationPresent(DefaultNull.class);
        if (hasDefault && hasDefaultNull) {
            throw new IllegalArgumentException(String.format("@Default and @DefaultNull present in [%s]"method.toGenericString()));
        }
        if (hasDefault) {
            defaultValue = method.getAnnotation(Default.class).value();
        }
        else if (!hasDefaultNull) {
            throw new IllegalArgumentException(String.format("No value present for '%s' in [%s]",
                    prettyPrint(annotation.value(), null),
                    method.toGenericString()));
        }
        final Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        final List<StringparamTokenList = new ArrayList<String>();
        for (Annotation[] parameterTab : parameterAnnotations) {
            for (Annotation parameter : parameterTab) {
                if (parameter.annotationType().equals(Param.class)) {
                    Param paramAnnotation = (Paramparameter;
                    paramTokenList.add(makeToken(paramAnnotation.value()));
                    break;
                }
            }
        }
        if (paramTokenList.size() != method.getParameterTypes().length) {
            throw new RuntimeException(String.format("Method [%s] is missing one or more @Param annotations",
                                                     method.toGenericString()));
        }
        final Object bulliedDefaultValue = .coerce(method.getGenericReturnType(), defaultValuemethod.getAnnotation(Separator.class));
        final String[] annotationValues = annotation.value();
        if (annotationValues == null || annotationValues.length == 0) {
            throw new IllegalArgumentException("Method " +
                                               method.toGenericString() +
                                               " declares config annotation but no field name!");
        }
        callbacks.add(new ConfigMagicMethodInterceptor(method,
                                                       ,
                                                       annotationValues,
                                                       paramTokenList,
                                                       ,
                                                       bulliedDefaultValue));
    }
    private String makeToken(String temp)
    {
        return "${" + temp + "}";
    }
    private String prettyPrint(String[] valuesfinal Map<StringStringmappedReplacements)
    {
        if (values == null || values.length == 0) {
            return "";
        }
        final StringBuilder sb = new StringBuilder("[");
        for (int i = 0; i < values.lengthi++) {
            sb.append(values[i]);
            if (i < (values.length - 1)) {
                sb.append(", ");
            }
        }
        sb.append(']');
        if (mappedReplacements != null && mappedReplacements.size() > 0) {
            sb.append(" translated to [");
            for (int i = 0; i < values.lengthi++) {
                sb.append(applyReplacements(values[i], mappedReplacements));
                if (i < (values.length - 1)) {
                    sb.append(", ");
                }
            }
            sb.append("]");
        }
        return sb.toString();
    }
    private static final class ConfigMagicFixedValue implements MethodInterceptor
    {
        private final Method method;
        private final String assignedFrom;
        private final Handler handler;
        private ConfigMagicFixedValue(final Method methodfinal String assignedFromfinal Object valuefinal boolean callSuper)
        {
            this. = method;
            this. = assignedFrom;
            // This is a workaround for broken cglib
            if (callSuper) {
                this. = new InvokeSuperHandler();
            }
            else {
                 = new FixedValueHandler(value);
            }
        }
        public Object intercept(Object oMethod methodObject[] objects, MethodProxy methodProxythrows Throwable
        {
            return .handle(methodProxyoobjects);
        }
        private static interface Handler
        {
            Object handle(MethodProxy mObject oObject[] argsthrows Throwable;
        }
        private static class InvokeSuperHandler implements Handler
        {
            public Object handle(MethodProxy mObject oObject[] argsthrows Throwable
            {
                return m.invokeSuper(oargs);
            }
        }
        private static class FixedValueHandler implements Handler
        {
            private final Object finalValue;
            public FixedValueHandler(final Object finalValue)
            {
                this. = finalValue;
            }
            public Object handle(MethodProxy mObject oObject[] argsthrows Throwable
            {
                return ;
            }
            private transient String toStringValue = null;
            @Override
            public String toString()
            {
                if ( == null) {
                    final StringBuilder sb = new StringBuilder("value: ");
                    if ( != null) {
                        sb.append(.toString());
                        sb.append(", class: ");
                        sb.append(.getClass().getName());
                    }
                    else {
                        sb.append("<null>");
                    }
                     = sb.toString();
                }
                return ;
            }
        }
        private transient String toStringValue = null;
        @Override
        public String toString()
        {
            if ( == null) {
                final StringBuilder sb = new StringBuilder(.getName());
                sb.append("(): ");
                sb.append();
                sb.append(", ");
                sb.append(.toString());
                 = sb.toString();
            }
            return ;
        }
    }
    private static final class ConfigMagicCallbackFilter implements CallbackFilter
    {
        private final Map<MethodIntegerslots;
        private ConfigMagicCallbackFilter(final Map<MethodIntegerslots)
        {
            this. = slots;
        }
        public int accept(Method method)
        {
            return .containsKey(method) ? .get(method) : 0;
        }
    }
    private static final class ConfigMagicMethodInterceptor implements MethodInterceptor
    {
        private final Method method;
        private final ConfigSource config;
        private final String[] properties;
        private final Bully bully;
        private final Object defaultValue;
        private final List<StringparamTokenList;
        private ConfigMagicMethodInterceptor(final Method method,
                                             final ConfigSource config,
                                             final String[] properties,
                                             final List<StringparamTokenList,
                                             final Bully bully,
                                             final Object defaultValue)
        {
            this. = method;
            this. = config;
            this. = properties;
            this. = paramTokenList;
            this. = bully;
            this. = defaultValue;
        }
        public Object intercept(final Object o,
                                final Method method,
                                final Object[] args,
                                final MethodProxy methodProxythrows Throwable
        {
            for (String property : ) {
                if (args.length == .size()) {
                    for (int i = 0; i < args.length; ++i) {
                        property = property.replace(.get(i), String.valueOf(args[i]));
                    }
                    String value = .getString(property);
                    if (value != null) {
                        .info("Assigning value [{}] for [{}] on [{}#{}()]",
                                    new Object[] { valuepropertymethod.getDeclaringClass().getName(), method.getName() });
                        return .coerce(method.getGenericReturnType(), valuemethod.getAnnotation(Separator.class));
                    }
                }
                else {
                    throw new IllegalStateException("Argument list doesn't match @Param list");
                }
            }
            .info("Assigning default value [{}] for {} on [{}#{}()]",
                        new Object[] { method.getDeclaringClass().getName(), method.getName() });
            return ;
        }
        private transient String toStringValue = null;
        @Override
        public String toString()
        {
            if ( == null) {
                 = .getName() + ": " + super.toString();
            }
            return ;
        }
    }
    private Method findToStringMethod(final Class<?> clazz)
    {
        try {
            return clazz.getMethod("toString"new Class [] {});
        }
        catch (NoSuchMethodException nsme) {
            try {
                return Object.class.getMethod("toString"new Class [] {});
            }
            catch (NoSuchMethodException nsme2) {
                throw new IllegalStateException("Could not intercept toString method!"nsme);
            }
        }
    }
    private static final class ConfigMagicBeanToString implements MethodInterceptor
    {
        private final List<Callback> callbacks;
        private transient String toStringValue = null;
        private ConfigMagicBeanToString(final List<Callback> callbacks)
        {
            this. = callbacks;
        }
        public Object intercept(final Object o,
                                final Method method,
                                final Object[] args,
                                final MethodProxy methodProxythrows Throwable
        {
            if ( == null) {
                final StringBuilder sb = new StringBuilder();
                for (int i = 2; i < .size(); i++) {
                    sb.append(.get(i).toString());
                    if (i < .size() - 1) {
                        sb.append("\n");
                    }
                }
                 = sb.toString();
            }
            return ;
        }
    }
New to GrepCode? Check out our FAQ X