// Path: src/main/java/com/github/gquintana/metrics/proxy/CGLibProxyFactory.java
// public class CGLibProxyFactory implements ProxyFactory {
// 
//     private final Map<ProxyClass, Class> proxyClasses = new ConcurrentHashMap<ProxyClass, Class>();
//     private static final Class[] ADAPTER_CALLBACK_TYPES = new Class[]{
//         AdapterMethodInterceptor.class,
//         AdapterLazyLoader.class
//     };
// 
//     private static class AdapterCallbackFilter implements CallbackFilter {
// 
//         private final ProxyHandler.InvocationFilter invocationFilter;
// 
//         private AdapterCallbackFilter(ProxyHandler.InvocationFilter invocationFilter) {
//             this.invocationFilter = invocationFilter;
//         }
// 
//         @Override
//         public int accept(Method method) {
//             // 0 is AdapterMethodInterceptor
//             // 1 is AdapterLazyLoader
//             return invocationFilter.isIntercepted(method) ? 0 : 1;
//         }
//     }
// 
//     private static class AdapterMethodInterceptor implements MethodInterceptor {
// 
//         private final ProxyHandler proxyHandler;
// 
//         private AdapterMethodInterceptor(ProxyHandler proxyHandler) {
//             this.proxyHandler = proxyHandler;
//         }
// 
//         @Override
//         public Object intercept(Object proxy, Method method, Object[] arguments, MethodProxy methodProxy) throws Throwable {
//             return proxyHandler.invoke(proxy, method, arguments);
//         }
//     }
// 
//     private static class AdapterLazyLoader<T> implements LazyLoader {
// 
//         private final T delegate;
// 
//         private AdapterLazyLoader(T delegate) {
//             this.delegate = delegate;
//         }
// 
//         @Override
//         public T loadObject() {
//             return delegate;
//         }
//     }
// 
//     private Class getProxyClass(ProxyHandler<?> proxyHandler, ProxyClass proxyClass) {
//         Class clazz = proxyClasses.get(proxyClass);
//         if (clazz == null) {
//             Enhancer enhancer = new Enhancer();
//             enhancer.setCallbackFilter(new AdapterCallbackFilter(proxyHandler.getInvocationFilter()));
//             enhancer.setCallbackTypes(ADAPTER_CALLBACK_TYPES);
//             enhancer.setClassLoader(proxyClass.getClassLoader());
//             enhancer.setInterfaces(proxyClass.getInterfaces());
//             clazz = enhancer.createClass();
//             proxyClasses.put(proxyClass, clazz);
//         }
//         return clazz;
//     }
// 
//     @Override
//     public <T> T newProxy(ProxyHandler<T> proxyHandler, ProxyClass proxyClass) {
//         try {
//             Object proxy = getProxyClass(proxyHandler, proxyClass).newInstance();
//             ((Factory) proxy).setCallbacks(new Callback[]{
//                 new AdapterMethodInterceptor(proxyHandler),
//                 new AdapterLazyLoader<Object>(proxyHandler.getDelegate())
//             });
//             return (T) proxy;
//         } catch (ReflectiveOperationException e) {
//             throw new ProxyException(e);
//         }
//     }
// }
// 
// Path: src/main/java/com/github/gquintana/metrics/proxy/CachingProxyFactory.java
// public class CachingProxyFactory implements ProxyFactory {
//     private final ConcurrentHashMap<ProxyClass, Constructor<?>> constructorCache = new ConcurrentHashMap<>();
//     @Override
//     public <T> T newProxy(ProxyHandler<T> proxyHandler, ProxyClass proxyClass) {
//         Constructor constructor = constructorCache.get(proxyClass);
//         if (constructor == null) {
//             constructor = proxyClass.createConstructor();
//             final Constructor oldConstructor = constructorCache.putIfAbsent(proxyClass, constructor);
//             constructor = oldConstructor == null ? constructor : oldConstructor;
//         }
//         try {
//             return (T) constructor.newInstance(proxyHandler);
//         } catch (ReflectiveOperationException reflectiveOperationException) {
//             throw new ProxyException(reflectiveOperationException);
//         }
//     }
//     /**
//      * Clears the constructor cache
//      */
//     public void clearCache() {
//         constructorCache.clear();
//     }
// }
// 
// Path: src/main/java/com/github/gquintana/metrics/proxy/ProxyFactory.java
// public interface ProxyFactory {
//     /**
//      * Create a proxy using given classloader and interfaces
//      *
//      * @param proxyHandler Proxy invocation handler
//      * @param proxyClass Class loader + Interfaces to implement
//      * @param <T> Proxy type
//      * @return Proxy
//      */
//     <T> T newProxy(ProxyHandler<T> proxyHandler, ProxyClass proxyClass);
// 
// }
// 
// Path: src/main/java/com/github/gquintana/metrics/proxy/ReflectProxyFactory.java
// public class ReflectProxyFactory implements ProxyFactory {
// 	/**
// 	 * {@inheritDoc}
// 	 */
//         @Override
// 	public <T> T newProxy(ProxyHandler<T> proxyHandler, ProxyClass proxyClass) {
// 		return (T) Proxy.newProxyInstance(proxyClass.getClassLoader(), proxyClass.getInterfaces(), proxyHandler);
// 	}
// }


import com.github.gquintana.metrics.proxy.CGLibProxyFactory;
import com.github.gquintana.metrics.proxy.CachingProxyFactory;
import com.github.gquintana.metrics.proxy.ProxyFactory;
import com.github.gquintana.metrics.proxy.ReflectProxyFactory;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
    }

    private static <T> Class<T> toClass(String className) {
        if (className == null) {
            return null;
        }
        try {
            @SuppressWarnings("unchecked")
            Class<T> clazz = (Class<T>) Class.forName(className);
            return clazz;
        } catch (ClassNotFoundException classNotFoundException) {
            throw new IllegalArgumentException("Property " + className + " is not a valid class", classNotFoundException);
        }
    }

    public Class<? extends Driver> getDriverClass() {
        return toClass(getProperty("metrics_driver"));
    }

    public Class<? extends ProxyFactory> getProxyFactoryClass() {
        Class<? extends ProxyFactory> factoryClass;
        String proxyFactoryName = getProperty("metrics_proxy_factory", "reflect");
        switch (proxyFactoryName) {
            case "reflect":
                factoryClass = ReflectProxyFactory.class;
                break;
            case "cglib":
                factoryClass = CGLibProxyFactory.class;
                break;
            case "caching":
// Path: src/main/java/com/github/gquintana/metrics/proxy/CGLibProxyFactory.java
// public class CGLibProxyFactory implements ProxyFactory {
// 
//     private final Map<ProxyClass, Class> proxyClasses = new ConcurrentHashMap<ProxyClass, Class>();
//     private static final Class[] ADAPTER_CALLBACK_TYPES = new Class[]{
//         AdapterMethodInterceptor.class,
//         AdapterLazyLoader.class
//     };
// 
//     private static class AdapterCallbackFilter implements CallbackFilter {
// 
//         private final ProxyHandler.InvocationFilter invocationFilter;
// 
//         private AdapterCallbackFilter(ProxyHandler.InvocationFilter invocationFilter) {
//             this.invocationFilter = invocationFilter;
//         }
// 
//         @Override
//         public int accept(Method method) {
//             // 0 is AdapterMethodInterceptor
//             // 1 is AdapterLazyLoader
//             return invocationFilter.isIntercepted(method) ? 0 : 1;
//         }
//     }
// 
//     private static class AdapterMethodInterceptor implements MethodInterceptor {
// 
//         private final ProxyHandler proxyHandler;
// 
//         private AdapterMethodInterceptor(ProxyHandler proxyHandler) {
//             this.proxyHandler = proxyHandler;
//         }
// 
//         @Override
//         public Object intercept(Object proxy, Method method, Object[] arguments, MethodProxy methodProxy) throws Throwable {
//             return proxyHandler.invoke(proxy, method, arguments);
//         }
//     }
// 
//     private static class AdapterLazyLoader<T> implements LazyLoader {
// 
//         private final T delegate;
// 
//         private AdapterLazyLoader(T delegate) {
//             this.delegate = delegate;
//         }
// 
//         @Override
//         public T loadObject() {
//             return delegate;
//         }
//     }
// 
//     private Class getProxyClass(ProxyHandler<?> proxyHandler, ProxyClass proxyClass) {
//         Class clazz = proxyClasses.get(proxyClass);
//         if (clazz == null) {
//             Enhancer enhancer = new Enhancer();
//             enhancer.setCallbackFilter(new AdapterCallbackFilter(proxyHandler.getInvocationFilter()));
//             enhancer.setCallbackTypes(ADAPTER_CALLBACK_TYPES);
//             enhancer.setClassLoader(proxyClass.getClassLoader());
//             enhancer.setInterfaces(proxyClass.getInterfaces());
//             clazz = enhancer.createClass();
//             proxyClasses.put(proxyClass, clazz);
//         }
//         return clazz;
//     }
// 
//     @Override
//     public <T> T newProxy(ProxyHandler<T> proxyHandler, ProxyClass proxyClass) {
//         try {
//             Object proxy = getProxyClass(proxyHandler, proxyClass).newInstance();
//             ((Factory) proxy).setCallbacks(new Callback[]{
//                 new AdapterMethodInterceptor(proxyHandler),
//                 new AdapterLazyLoader<Object>(proxyHandler.getDelegate())
//             });
//             return (T) proxy;
//         } catch (ReflectiveOperationException e) {
//             throw new ProxyException(e);
//         }
//     }
// }
// 
// Path: src/main/java/com/github/gquintana/metrics/proxy/CachingProxyFactory.java
// public class CachingProxyFactory implements ProxyFactory {
//     private final ConcurrentHashMap<ProxyClass, Constructor<?>> constructorCache = new ConcurrentHashMap<>();
//     @Override
//     public <T> T newProxy(ProxyHandler<T> proxyHandler, ProxyClass proxyClass) {
//         Constructor constructor = constructorCache.get(proxyClass);
//         if (constructor == null) {
//             constructor = proxyClass.createConstructor();
//             final Constructor oldConstructor = constructorCache.putIfAbsent(proxyClass, constructor);
//             constructor = oldConstructor == null ? constructor : oldConstructor;
//         }
//         try {
//             return (T) constructor.newInstance(proxyHandler);
//         } catch (ReflectiveOperationException reflectiveOperationException) {
//             throw new ProxyException(reflectiveOperationException);
//         }
//     }
//     /**
//      * Clears the constructor cache
//      */
//     public void clearCache() {
//         constructorCache.clear();
//     }
// }
// 
// Path: src/main/java/com/github/gquintana/metrics/proxy/ProxyFactory.java
// public interface ProxyFactory {
//     /**
//      * Create a proxy using given classloader and interfaces
//      *
//      * @param proxyHandler Proxy invocation handler
//      * @param proxyClass Class loader + Interfaces to implement
//      * @param <T> Proxy type
//      * @return Proxy
//      */
//     <T> T newProxy(ProxyHandler<T> proxyHandler, ProxyClass proxyClass);
// 
// }
// 
// Path: src/main/java/com/github/gquintana/metrics/proxy/ReflectProxyFactory.java
// public class ReflectProxyFactory implements ProxyFactory {
// 	/**
// 	 * {@inheritDoc}
// 	 */
//         @Override
// 	public <T> T newProxy(ProxyHandler<T> proxyHandler, ProxyClass proxyClass) {
// 		return (T) Proxy.newProxyInstance(proxyClass.getClassLoader(), proxyClass.getInterfaces(), proxyHandler);
// 	}
// }


// Path: src/main/java/com/github/gquintana/metrics/sql/DriverUrl.java
import com.github.gquintana.metrics.proxy.CGLibProxyFactory;
import com.github.gquintana.metrics.proxy.CachingProxyFactory;
import com.github.gquintana.metrics.proxy.ProxyFactory;
import com.github.gquintana.metrics.proxy.ReflectProxyFactory;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

    }

    private static <T> Class<T> toClass(String className) {
        if (className == null) {
            return null;
        }
        try {
            @SuppressWarnings("unchecked")
            Class<T> clazz = (Class<T>) Class.forName(className);
            return clazz;
        } catch (ClassNotFoundException classNotFoundException) {
            throw new IllegalArgumentException("Property " + className + " is not a valid class", classNotFoundException);
        }
    }

    public Class<? extends Driver> getDriverClass() {
        return toClass(getProperty("metrics_driver"));
    }

    public Class<? extends ProxyFactory> getProxyFactoryClass() {
        Class<? extends ProxyFactory> factoryClass;
        String proxyFactoryName = getProperty("metrics_proxy_factory", "reflect");
        switch (proxyFactoryName) {
            case "reflect":
                factoryClass = ReflectProxyFactory.class;
                break;
            case "cglib":
                factoryClass = CGLibProxyFactory.class;
                break;
            case "caching":
                factoryClass = CachingProxyFactory.class;