Improvements to ClassMeta/BeanContext (2).

Project: http://git-wip-us.apache.org/repos/asf/incubator-juneau/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-juneau/commit/062d7ddc
Tree: http://git-wip-us.apache.org/repos/asf/incubator-juneau/tree/062d7ddc
Diff: http://git-wip-us.apache.org/repos/asf/incubator-juneau/diff/062d7ddc

Branch: refs/heads/master
Commit: 062d7ddc65b462128c43b9673c20104dcfd58e80
Parents: c8fa4ed
Author: JamesBognar <[email protected]>
Authored: Sat Feb 4 17:44:09 2017 -0500
Committer: JamesBognar <[email protected]>
Committed: Sat Feb 4 17:44:09 2017 -0500

----------------------------------------------------------------------
 .../java/org/apache/juneau/BeanContext.java     |  48 +++-
 .../org/apache/juneau/BeanPropertyMeta.java     |   8 +-
 .../main/java/org/apache/juneau/ClassMeta.java  | 237 +++++++++++++------
 .../org/apache/juneau/transform/PojoSwap.java   |   6 +
 4 files changed, 207 insertions(+), 92 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/062d7ddc/juneau-core/src/main/java/org/apache/juneau/BeanContext.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/BeanContext.java 
b/juneau-core/src/main/java/org/apache/juneau/BeanContext.java
index 05006f3..1cb9180 100644
--- a/juneau-core/src/main/java/org/apache/juneau/BeanContext.java
+++ b/juneau-core/src/main/java/org/apache/juneau/BeanContext.java
@@ -1155,8 +1155,8 @@ public class BeanContext extends Context {
 
                if (! cmCacheCache.containsKey(hashCode)) {
                        ConcurrentHashMap<Class,ClassMeta> cm = new 
ConcurrentHashMap<Class,ClassMeta>();
-                       cm.put(String.class, new ClassMeta(String.class, this));
-                       cm.put(Object.class, new ClassMeta(Object.class, this));
+                       cm.put(String.class, new ClassMeta(String.class, this, 
null, null, findPojoSwap(String.class), findChildPojoSwaps(String.class)));
+                       cm.put(Object.class, new ClassMeta(Object.class, this, 
null, null, findPojoSwap(Object.class), findChildPojoSwaps(Object.class)));
                        cmCacheCache.putIfAbsent(hashCode, cm);
                }
                this.cmCache = cmCacheCache.get(hashCode);
@@ -1385,7 +1385,7 @@ public class BeanContext extends Context {
                // Note that if it has a pojo swap, we still want to cache it 
so that
                // we can cache something like byte[] with ByteArrayBase64Swap.
                if (c.isArray() && findPojoSwap(c) == null)
-                       return new ClassMeta(c, this);
+                       return new ClassMeta(c, this, findImplClass(c), 
findBeanFilter(c), findPojoSwap(c), findChildPojoSwaps(c));
 
                // This can happen if we have transforms defined against String 
or Object.
                if (cmCache == null)
@@ -1407,7 +1407,7 @@ public class BeanContext extends Context {
                                                if (pcm.innerClass == c)
                                                        return pcm;
 
-                                       cm = new ClassMeta<T>(c, this, true);
+                                       cm = new ClassMeta<T>(c, this, 
findImplClass(c), findBeanFilter(c), findPojoSwap(c), findChildPojoSwaps(c), 
true);
                                        pendingClassMetas.addLast(cm);
                                        try {
                                                cm.init();
@@ -1751,7 +1751,7 @@ public class BeanContext extends Context {
         * @param c The class associated with the swap.
         * @return The swap associated with the class, or null if there is no 
association.
         */
-       protected final <T> PojoSwap findPojoSwap(Class<T> c) {
+       private final <T> PojoSwap findPojoSwap(Class<T> c) {
                // Note:  On first
                if (c != null)
                        for (PojoSwap f : pojoSwaps)
@@ -1765,12 +1765,18 @@ public class BeanContext extends Context {
         * @param c The class to check.
         * @return <jk>true</jk> if the specified class or one of its 
subclasses has a {@link PojoSwap} associated with it.
         */
-       protected final boolean hasChildPojoSwaps(Class<?> c) {
-               if (c != null)
-                       for (PojoSwap f : pojoSwaps)
-                               if (isParentClass(c, f.getNormalClass()))
-                                       return true;
-               return false;
+       private final PojoSwap[] findChildPojoSwaps(Class<?> c) {
+               if (c == null || pojoSwaps.length == 0)
+                       return null;
+               List<PojoSwap> l = null;
+               for (PojoSwap f : pojoSwaps) {
+                       if (isParentClass(c, f.getNormalClass())) {
+                               if (l == null)
+                                       l = new ArrayList<PojoSwap>();
+                               l.add(f);
+                       }
+               }
+               return l == null ? null : l.toArray(new PojoSwap[l.size()]);
        }
 
        /**
@@ -1781,7 +1787,7 @@ public class BeanContext extends Context {
         * @param c The class associated with the bean filter.
         * @return The bean filter associated with the class, or null if there 
is no association.
         */
-       protected final <T> BeanFilter findBeanFilter(Class<T> c) {
+       private final <T> BeanFilter findBeanFilter(Class<T> c) {
                if (c != null)
                        for (BeanFilter f : beanFilters)
                                if (isParentClass(f.getBeanClass(), c))
@@ -1833,6 +1839,24 @@ public class BeanContext extends Context {
                return null;
        }
 
+       private final <T> Class<? extends T> findImplClass(Class<T> c) {
+               if (implClasses.isEmpty())
+                       return null;
+               Class cc = c;
+               while (cc != null) {
+                       Class implClass = implClasses.get(cc);
+                       if (implClass != null)
+                               return implClass;
+                       for (Class ic : cc.getInterfaces()) {
+                               implClass = implClasses.get(ic);
+                               if (implClass != null)
+                                       return implClass;
+                       }
+                       cc = cc.getSuperclass();
+               }
+               return null;
+       }
+
        /**
         * Returns a reusable {@link ClassMeta} representation for the class 
<code>Object</code>.
         * <p>

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/062d7ddc/juneau-core/src/main/java/org/apache/juneau/BeanPropertyMeta.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/BeanPropertyMeta.java 
b/juneau-core/src/main/java/org/apache/juneau/BeanPropertyMeta.java
index ce7fdb5..a9e52f0 100644
--- a/juneau-core/src/main/java/org/apache/juneau/BeanPropertyMeta.java
+++ b/juneau-core/src/main/java/org/apache/juneau/BeanPropertyMeta.java
@@ -848,9 +848,7 @@ public class BeanPropertyMeta {
                        return null;
                // Otherwise, look it up via bean context.
                if (rawTypeMeta.hasChildPojoSwaps()) {
-                       Class c = o.getClass();
-                       ClassMeta<?> cm = rawTypeMeta.innerClass == c ? 
rawTypeMeta : beanContext.getClassMeta(c);
-                       PojoSwap f = cm.getPojoSwap();
+                       PojoSwap f = 
rawTypeMeta.getChildPojoSwapForSwap(o.getClass());
                        if (f != null)
                                return f.swap(session, o);
                }
@@ -863,9 +861,7 @@ public class BeanPropertyMeta {
                if (o == null)
                        return null;
                if (rawTypeMeta.hasChildPojoSwaps()) {
-                       Class c = o.getClass();
-                       ClassMeta<?> cm = rawTypeMeta.innerClass == c ? 
rawTypeMeta : beanContext.getClassMeta(c);
-                       PojoSwap f = cm.getPojoSwap();
+                       PojoSwap f = 
rawTypeMeta.getChildPojoSwapForUnswap(o.getClass());
                        if (f != null)
                                return f.unswap(session, o, rawTypeMeta);
                }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/062d7ddc/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java 
b/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java
index e98f595..5e079c4 100644
--- a/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java
+++ b/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java
@@ -21,6 +21,7 @@ import java.lang.reflect.Proxy;
 import java.net.*;
 import java.net.URI;
 import java.util.*;
+import java.util.concurrent.*;
 
 import org.apache.juneau.annotation.*;
 import org.apache.juneau.internal.*;
@@ -57,9 +58,10 @@ public final class ClassMeta<T> implements Type {
        }
 
        final Class<T> innerClass;                              // The class 
being wrapped.
+       final Class<? extends T> implClass;                     // The 
implementation class to use if this is an interface.
        final ClassCategory cc;                                 // The class 
category.
        final Method fromStringMethod;                          // The static 
valueOf(String) or fromString(String) or forString(String) method (if it has 
one).
-       Constructor<? extends T>
+       final Constructor<? extends T>
                noArgConstructor;                                    // The 
no-arg constructor for this class (if it has one).
        final Constructor<T>
                stringConstructor,                                   // The 
X(String) constructor (if it has one).
@@ -82,7 +84,10 @@ public final class ClassMeta<T> implements Type {
        final Map<String,Method>
                remoteableMethods,                                   // Methods 
annotated with @Remoteable.  Contains all public methods if class is annotated 
with @Remotable.
                publicMethods;                                       // All 
public methods, including static methods.
-
+       final PojoSwap<?,?>[] childPojoSwaps;                   // Any 
PojoSwaps where the normal type is a subclass of this class.
+       final ConcurrentHashMap<Class<?>,PojoSwap<?,?>>
+               childSwapMap,                                        // Maps 
normal subclasses to PojoSwaps.
+               childUnswapMap;                                      // Maps 
swap subclasses to PojoSwaps.
 
        final BeanContext beanContext;                    // The bean context 
that created this object.
        ClassMeta<?>
@@ -99,7 +104,6 @@ public final class ClassMeta<T> implements Type {
 
        private MetadataMap extMeta = new MetadataMap();  // Extended metadata
        private Throwable initException;                  // Any exceptions 
thrown in the init() method.
-       private boolean hasChildPojoSwaps;                // True if this class 
or any subclass of this class has a PojoSwap associated with it.
 
        private static final Boolean BOOLEAN_DEFAULT = false;
        private static final Character CHARACTER_DEFAULT = (char)0;
@@ -113,8 +117,8 @@ public final class ClassMeta<T> implements Type {
        /**
         * Shortcut for calling <code>ClassMeta(innerClass, beanContext, 
<jk>false</jk>)</code>.
         */
-       ClassMeta(Class<T> innerClass, BeanContext beanContext) {
-               this(innerClass, beanContext, false);
+       ClassMeta(Class<T> innerClass, BeanContext beanContext, Class<? extends 
T> implClass, BeanFilter beanFilter, PojoSwap<T,?> pojoSwap, PojoSwap<?,?>[] 
childPojoSwaps) {
+               this(innerClass, beanContext, implClass, beanFilter, pojoSwap, 
childPojoSwaps, false);
        }
 
        /**
@@ -122,13 +126,26 @@ public final class ClassMeta<T> implements Type {
         *
         * @param innerClass The class being wrapped.
         * @param beanContext The bean context that created this object.
+        * @param implClass For interfaces and abstract classes, this 
represents the "real" class to instantiate.
+        *      Can be <jk>null</jk>.
+        * @param beanFilter The {@link BeanFilter} programmatically associated 
with this class.
+        *      Can be <jk>null</jk>.
+        * @param pojoSwap The {@link PojoSwap} programmatically associated 
with this class.
+        *      Can be <jk>null</jk>.
+        * @param childPojoSwap The child {@link PojoSwap PojoSwaps} 
programmatically associated with this class.
+        *      These are the <code>PojoSwaps</code> that have normal classes 
that are subclasses of this class.
+        *      Can be <jk>null</jk>.
         * @param delayedInit Don't call init() in constructor.
         *      Used for delayed initialization when the possibility of class 
reference loops exist.
         */
        @SuppressWarnings({ "rawtypes", "unchecked" })
-       ClassMeta(Class<T> innerClass, BeanContext beanContext, boolean 
delayedInit) {
+       ClassMeta(Class<T> innerClass, BeanContext beanContext, Class<? extends 
T> implClass, BeanFilter beanFilter, PojoSwap<T,?> pojoSwap, PojoSwap<?,?>[] 
childPojoSwaps, boolean delayedInit) {
                this.innerClass = innerClass;
                this.beanContext = beanContext;
+               this.implClass = implClass;
+               this.childPojoSwaps = childPojoSwaps;
+               this.childSwapMap = childPojoSwaps == null ? null : new 
ConcurrentHashMap<Class<?>,PojoSwap<?,?>>();
+               this.childUnswapMap = childPojoSwaps == null ? null : new 
ConcurrentHashMap<Class<?>,PojoSwap<?,?>>();
 
                Class<T> c = innerClass;
                ClassCategory _cc = ClassCategory.OTHER;
@@ -351,6 +368,41 @@ public final class ClassMeta<T> implements Type {
                        }
                }
 
+               if (innerClass != Object.class) {
+                       _noArgConstructor = 
(Constructor<T>)findNoArgConstructor(implClass == null ? innerClass : 
implClass, Visibility.PUBLIC);
+               }
+
+               if (beanFilter == null)
+                       beanFilter = findBeanFilter();
+
+               PojoSwap ps = null;
+               if (_swapMethod != null) {
+                       ps = new PojoSwap<T,Object>(c, 
_swapMethod.getReturnType()) {
+                               @Override
+                               public Object swap(BeanSession session, Object 
o) throws SerializeException {
+                                       try {
+                                               return swapMethod.invoke(o, 
session);
+                                       } catch (Exception e) {
+                                               throw new SerializeException(e);
+                                       }
+                               }
+                               @Override
+                               public T unswap(BeanSession session, Object f, 
ClassMeta<?> hint) throws ParseException {
+                                       try {
+                                               if (swapConstructor != null)
+                                                       return 
swapConstructor.newInstance(f);
+                                               return super.unswap(session, f, 
hint);
+                                       } catch (Exception e) {
+                                               throw new ParseException(e);
+                                       }
+                               }
+                       };
+               }
+               if (ps == null)
+                       ps = findPojoSwap();
+               if (ps == null)
+                       ps = pojoSwap;
+
                this.cc = _cc;
                this.isDelegate = _isDelegate;
                this.fromStringMethod = _fromStringMethod;
@@ -368,6 +420,8 @@ public final class ClassMeta<T> implements Type {
                this.primitiveDefault = _primitiveDefault;
                this.publicMethods = _publicMethods;
                this.remoteableMethods = _remoteableMethods;
+               this.beanFilter = beanFilter;
+               this.pojoSwap = ps;
 
                if (! delayedInit)
                        init();
@@ -379,6 +433,10 @@ public final class ClassMeta<T> implements Type {
         */
        ClassMeta(ClassMeta<T> mainType, ClassMeta<?> keyType, ClassMeta<?> 
valueType, ClassMeta<?> elementType) {
                this.innerClass = mainType.innerClass;
+               this.implClass = mainType.implClass;
+               this.childPojoSwaps = mainType.childPojoSwaps;
+               this.childSwapMap = mainType.childSwapMap;
+               this.childUnswapMap = mainType.childUnswapMap;
                this.cc = mainType.cc;
                this.fromStringMethod = mainType.fromStringMethod;
                this.noArgConstructor = mainType.noArgConstructor;
@@ -412,23 +470,12 @@ public final class ClassMeta<T> implements Type {
                this.beanFilter = mainType.beanFilter;
                this.extMeta = mainType.extMeta;
                this.initException = mainType.initException;
-               this.hasChildPojoSwaps = mainType.hasChildPojoSwaps;
        }
 
        @SuppressWarnings({ "unchecked", "rawtypes" })
        ClassMeta init() {
 
                try {
-                       beanFilter = findBeanFilter(beanContext);
-                       pojoSwap = findPojoSwap(beanContext);
-
-                       if (innerClass != Object.class) {
-                               this.noArgConstructor = 
beanContext.getImplClassConstructor(innerClass, Visibility.PUBLIC);
-                               if (noArgConstructor == null)
-                                       noArgConstructor = 
findNoArgConstructor(innerClass, Visibility.PUBLIC);
-                       }
-
-                       this.hasChildPojoSwaps = 
beanContext.hasChildPojoSwaps(innerClass);
 
                        Class c = innerClass;
 
@@ -528,6 +575,35 @@ public final class ClassMeta<T> implements Type {
                return beanContext.findParameters(innerClass, innerClass);
        }
 
+       private BeanFilter findBeanFilter() {
+               try {
+                       List<Bean> ba = 
ReflectionUtils.findAnnotations(Bean.class, innerClass);
+                       if (! ba.isEmpty())
+                               return new 
AnnotationBeanFilterBuilder(innerClass, ba).build();
+               } catch (Exception e) {
+                       throw new RuntimeException(e);
+               }
+               return null;
+       }
+
+       @SuppressWarnings("unchecked")
+       private PojoSwap<T,?> findPojoSwap() {
+               try {
+                       Pojo p = innerClass.getAnnotation(Pojo.class);
+                       if (p != null) {
+                               Class<?> c = p.swap();
+                               if (c != Null.class) {
+                                       if 
(ClassUtils.isParentClass(PojoSwap.class, c))
+                                               return 
(PojoSwap<T,?>)c.newInstance();
+                                       throw new RuntimeException("TODO - 
Surrogate classes not yet supported.");
+                               }
+                       }
+                       return null;
+               } catch (Exception e) {
+                       throw new RuntimeException(e);
+               }
+       }
+
        /**
         * Returns the bean dictionary name associated with this class.
         * <p>
@@ -597,45 +673,58 @@ public final class ClassMeta<T> implements Type {
         *
         * @return <jk>true</jk> if this class or any child classes has a 
{@link PojoSwap} associated with it.
         */
-       public boolean hasChildPojoSwaps() {
-               return hasChildPojoSwaps;
+       protected boolean hasChildPojoSwaps() {
+               return childPojoSwaps != null;
        }
 
-       private BeanFilter findBeanFilter(BeanContext context) {
-               try {
-                       if (context == null)
+       /**
+        * Returns the {@link PojoSwap} where the specified class is the 
same/subclass of the normal class of
+        * one of the child pojo swaps associated with this class.
+        *
+        * @param normalClass The normal class being resolved.
+        * @return The resolved {@link PojoSwap} or <jk>null</jk> if none were 
found.
+        */
+       protected PojoSwap<?,?> getChildPojoSwapForSwap(Class<?> normalClass) {
+               if (childSwapMap != null) {
+                       PojoSwap<?,?> s = childSwapMap.get(normalClass);
+                       if (s == null) {
+                               for (PojoSwap<?,?> f : childPojoSwaps)
+                                       if (s == null && 
isParentClass(f.getNormalClass(), normalClass))
+                                               s = f;
+                               if (s == null)
+                                        s = PojoSwap.NULL;
+                               childSwapMap.putIfAbsent(normalClass, s);
+                       }
+                       if (s == PojoSwap.NULL)
                                return null;
-                       BeanFilter f = context.findBeanFilter(innerClass);
-                       if (f != null)
-                               return f;
-                       List<Bean> ba = 
ReflectionUtils.findAnnotations(Bean.class, innerClass);
-                       if (! ba.isEmpty())
-                               f = new AnnotationBeanFilterBuilder(innerClass, 
ba).build();
-                       return f;
-               } catch (Exception e) {
-                       throw new RuntimeException(e);
+                       return s;
                }
+               return null;
        }
 
-       @SuppressWarnings("unchecked")
-       private PojoSwap<T,?> findPojoSwap(BeanContext context) {
-               try {
-                       Pojo p = innerClass.getAnnotation(Pojo.class);
-                       if (p != null) {
-                               Class<?> c = p.swap();
-                               if (c != Null.class) {
-                                       if 
(ClassUtils.isParentClass(PojoSwap.class, c))
-                                               return 
(PojoSwap<T,?>)c.newInstance();
-                                       throw new RuntimeException("TODO - 
Surrogate classes not yet supported.");
-                               }
+       /**
+        * Returns the {@link PojoSwap} where the specified class is the 
same/subclass of the swap class of
+        * one of the child pojo swaps associated with this class.
+        *
+        * @param swapClass The swap class being resolved.
+        * @return The resolved {@link PojoSwap} or <jk>null</jk> if none were 
found.
+        */
+       protected PojoSwap<?,?> getChildPojoSwapForUnswap(Class<?> swapClass) {
+               if (childUnswapMap != null) {
+                       PojoSwap<?,?> s = childUnswapMap.get(swapClass);
+                       if (s == null) {
+                               for (PojoSwap<?,?> f : childPojoSwaps)
+                                       if (s == null && 
isParentClass(f.getSwapClass(), swapClass))
+                                               s = f;
+                               if (s == null)
+                                        s = PojoSwap.NULL;
+                               childUnswapMap.putIfAbsent(swapClass, s);
                        }
-                       if (context == null)
+                       if (s == PojoSwap.NULL)
                                return null;
-                       PojoSwap<T,?> f = context.findPojoSwap(innerClass);
-                       return f;
-               } catch (Exception e) {
-                       throw new RuntimeException(e);
+                       return s;
                }
+               return null;
        }
 
        /**
@@ -1512,31 +1601,31 @@ public final class ClassMeta<T> implements Type {
        public int hashCode() {
                return super.hashCode();
        }
-
-       public abstract static class CreateSession {
-               LinkedList<ClassMeta<?>> stack;
-
-               public CreateSession push(ClassMeta<?> cm) {
-                       if (stack == null)
-                               stack = new LinkedList<ClassMeta<?>>();
-                       stack.add(cm);
-                       return this;
-               }
-
-               public CreateSession pop(ClassMeta<?> expected) {
-                       if (stack == null || stack.removeLast() != expected)
-                               throw new 
BeanRuntimeException("ClassMetaSession creation stack corruption!");
-                       return this;
-               }
-
-               public <T> ClassMeta<T> getClassMeta(Class<T> c) {
-                       if (stack != null)
-                               for (ClassMeta<?> cm : stack)
-                                       if (cm.innerClass == c)
-                                               return (ClassMeta<T>)cm;
-                       return createClassMeta(c);
-               }
-
-               public abstract <T> ClassMeta<T> createClassMeta(Class<T> c);
-       }
+//
+//     public abstract static class CreateSession {
+//             LinkedList<ClassMeta<?>> stack;
+//
+//             public CreateSession push(ClassMeta<?> cm) {
+//                     if (stack == null)
+//                             stack = new LinkedList<ClassMeta<?>>();
+//                     stack.add(cm);
+//                     return this;
+//             }
+//
+//             public CreateSession pop(ClassMeta<?> expected) {
+//                     if (stack == null || stack.removeLast() != expected)
+//                             throw new 
BeanRuntimeException("ClassMetaSession creation stack corruption!");
+//                     return this;
+//             }
+//
+//             public <T> ClassMeta<T> getClassMeta(Class<T> c) {
+//                     if (stack != null)
+//                             for (ClassMeta<?> cm : stack)
+//                                     if (cm.innerClass == c)
+//                                             return (ClassMeta<T>)cm;
+//                     return createClassMeta(c);
+//             }
+//
+//             public abstract <T> ClassMeta<T> createClassMeta(Class<T> c);
+//     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/062d7ddc/juneau-core/src/main/java/org/apache/juneau/transform/PojoSwap.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/transform/PojoSwap.java 
b/juneau-core/src/main/java/org/apache/juneau/transform/PojoSwap.java
index ec97dd8..5a1d104 100644
--- a/juneau-core/src/main/java/org/apache/juneau/transform/PojoSwap.java
+++ b/juneau-core/src/main/java/org/apache/juneau/transform/PojoSwap.java
@@ -98,6 +98,12 @@ import org.apache.juneau.serializer.*;
  */
 public abstract class PojoSwap<T,S> {
 
+       /**
+        * Represents a non-existent pojo swap.
+        */
+       @SuppressWarnings({ "unchecked", "rawtypes" })
+       public final static PojoSwap NULL = new PojoSwap(null, null) {};
+
        private final Class<T> normalClass;
        private final Class<?> swapClass;
        private ClassMeta<?> swapClassMeta;

Reply via email to