001    package org.andromda.cartridges.support.webservice.client;
002    
003    import java.lang.reflect.Array;
004    import java.lang.reflect.Method;
005    import java.util.ArrayList;
006    import java.util.Collection;
007    import java.util.HashMap;
008    import java.util.Iterator;
009    import java.util.List;
010    import java.util.Map;
011    import javax.wsdl.Definition;
012    import javax.wsdl.Types;
013    import javax.wsdl.extensions.schema.Schema;
014    import javax.xml.namespace.QName;
015    import org.apache.axiom.om.OMAbstractFactory;
016    import org.apache.axiom.om.OMElement;
017    import org.apache.axiom.om.OMFactory;
018    import org.apache.axiom.om.OMNamespace;
019    import org.apache.axiom.om.util.Base64;
020    import org.apache.commons.beanutils.PropertyUtils;
021    import org.apache.commons.lang.StringUtils;
022    import org.w3c.dom.Element;
023    import org.w3c.dom.Node;
024    import org.w3c.dom.NodeList;
025    
026    /**
027     * Utilities for Axis2 clients.
028     *
029     * @author Chad Brandon
030     */
031    public class Axis2ClientUtils
032    {
033        /**
034         * Gets the appropriate OM element for the given method
035         * @param definition the WSDL definition.
036         * @param method the method corresponding to the OMElement to create.
037         * @param arguments the arguments to pass to the method.
038         * @param typeMapper the {@link TypeMapper} instance to use for converting types.
039         * @return the constructed OMElement
040         */
041        public static OMElement getOperationOMElement(
042            final Definition definition,
043            final Method method,
044            final Object[] arguments,
045            final TypeMapper typeMapper)
046        {
047            final Class serviceClass = method.getDeclaringClass();
048            OMElement operationOMElement = null;
049            try
050            {
051                final String operationName = method.getName();
052                final String serviceName = serviceClass.getSimpleName();
053                final Element operationElement = getElementByAttribute(
054                        definition,
055                        NAME,
056                        operationName);
057                final Schema operationSchema = Axis2ClientUtils.getElementSchemaByAttribute(
058                        definition,
059                        NAME,
060                        operationName);
061                if (operationSchema != null)
062                {
063                    final OMFactory factory = OMAbstractFactory.getOMFactory();
064    
065                    // - collect all the namespaces
066                    final Map<String, OMNamespace> namespaces = new HashMap<String, OMNamespace>();
067                    final OMNamespace xsiNamespace = factory.createOMNamespace(XSI_NS, XSI_PREFIX);
068                    namespaces.put(XSI_PREFIX, xsiNamespace);
069    
070                    final Collection<Schema> schemas = getSchemas(definition);
071                    for (final Schema schema : schemas)
072                    {
073                        final String namespace = Axis2ClientUtils.getTargetNamespace(schema);
074                        final String prefix = getNamespacePrefix(
075                                definition,
076                                namespace);
077                        namespaces.put(prefix, factory.createOMNamespace(namespace, prefix));
078                    }
079    
080                    operationOMElement =
081                        getOMElement(
082                            definition,
083                            operationSchema,
084                            null,
085                            null,
086                            operationName,
087                            factory,
088                            namespaces,
089                            typeMapper);
090                    if (operationElement == null)
091                    {
092                        throw new RuntimeException("No operation with name '" + operationName + "' can be found on service: " +
093                            serviceName);
094                    }
095                    final List argumentElements = Axis2ClientUtils.getElementsWithAttribute(
096                            operationElement,
097                            TYPE);
098                    final Class[] parameterTypes = method.getParameterTypes();
099                    if (argumentElements.size() != arguments.length)
100                    {
101                        throw new RuntimeException("Operation: " + operationName + " takes " + parameterTypes.length +
102                            " argument(s), and 'arguments' only contains: " + arguments.length);
103                    }
104    
105                    // - declare all the namespaces
106                    operationOMElement.declareNamespace(xsiNamespace);
107                    for (final OMNamespace namespace : namespaces.values())
108                    {
109                        operationOMElement.declareNamespace(namespace);
110                    }
111    
112                    // - add the argument children
113                    for (int ctr = 0; ctr < argumentElements.size(); ctr++)
114                    {
115                        final Element argument = (Element)argumentElements.get(ctr);
116                        final String argumentName = getAttributeValue(
117                                argument,
118                                NAME);
119                        final OMElement element = getOMElement(
120                                definition,
121                                operationSchema,
122                                argument,
123                                arguments[ctr],
124                                argumentName,
125                                factory,
126                                namespaces,
127                                typeMapper);
128                        operationOMElement.addChild(element);
129                    }
130                }
131            }
132            catch (Exception exception)
133            {
134                throw new RuntimeException(exception);
135            }
136            return operationOMElement;
137        }
138    
139        /**
140         * Constructs and OMElement from the given bean
141         *
142         * @param definition the WSDL definition
143         * @param schema the current schema from which to retrieve the om element.
144         * @param componentElement the current componentElemnet of the WSDL definition.
145         * @param bean the bean to introspect
146         * @param elementName the name of the element to construct.
147         * @param factory the factory used for element construction.
148         * @param namespaces all available namespaces.
149         * @param typeMapper the {@link TypeMapper} instance to use for converting types.
150         * @return OMElement
151         */
152        public static OMElement getOMElement(
153            final Definition definition,
154            final Schema schema,
155            final Element componentElement,
156            final Object bean,
157            final String elementName,
158            final OMFactory factory,
159            final Map<String, OMNamespace> namespaces,
160            final TypeMapper typeMapper)
161        {
162            return getOMElement(
163                definition,
164                schema,
165                componentElement,
166                bean,
167                elementName,
168                factory,
169                namespaces,
170                typeMapper,
171                new ArrayList<Object>());
172        }
173    
174        /**
175         * Constructs and OMElement from the given bean
176         *
177         * @param definition the WSDL definition
178         * @param schema the current schema from which to retrieve the om element.
179         * @param componentElement the current componentElemnet of the WSDL definition.
180         * @param bean the bean to introspect
181         * @param elementName the name of the element
182         * @param factory the OM factory instance used to create the element.
183         * @param namespaces all available namespaces.
184         * @param typeMapper the {@link TypeMapper} instance to use for converting types.
185         * @param evaluatingBeans the collection in which to keep the beans that are evaluating in order
186         *        to prevent endless recursion.
187         */
188        private static OMElement getOMElement(
189            final Definition definition,
190            Schema schema,
191            final Element componentElement,
192            final Object bean,
193            final String elementName,
194            final OMFactory factory,
195            final Map<String, OMNamespace> namespaces,
196            final TypeMapper typeMapper,
197            final Collection<Object> evaluatingBeans)
198        {
199            final String componentElementName = componentElement != null ? componentElement.getAttribute(NAME) : null;
200            if (StringUtils.isNotBlank(componentElementName))
201            {
202                final Schema currentSchema = Axis2ClientUtils.getElementSchemaByAttribute(
203                        definition,
204                        NAME,
205                        componentElementName);
206                if (currentSchema != null)
207                {
208                    schema = currentSchema;
209                }
210            }
211    
212            OMNamespace omNamespace = null;
213            if (isQualified(schema))
214            {
215                final String namespace = Axis2ClientUtils.getTargetNamespace(schema);
216                final String namespacePrefix = getNamespacePrefix(
217                        definition,
218                        namespace);
219                omNamespace = namespaces.get(namespacePrefix);
220            }
221    
222            final OMElement omElement =
223                factory.createOMElement(
224                    elementName,
225                    omNamespace);
226            if (bean != null && evaluatingBeans != null && !evaluatingBeans.contains(bean))
227            {
228                evaluatingBeans.add(bean);
229                if (isSimpleType(bean, typeMapper))
230                {
231                    omElement.addChild(factory.createOMText(typeMapper.getStringValue(bean)));
232                }
233                else if (bean instanceof byte[])
234                {
235                    omElement.addChild(factory.createOMText(Base64.encode((byte[])bean)));
236                }
237                else
238                {
239                    final Element currentComponentElement =
240                        Axis2ClientUtils.getElementByAttribute(
241                            definition,
242                            NAME,
243                            bean.getClass().getSimpleName());
244                    final Class beanType = bean.getClass();
245                    if (beanType.isArray())
246                    {
247                        final Element arrayElement = Axis2ClientUtils.getRequiredElementByAttribute(
248                                definition,
249                                componentElement,
250                                NAME,
251                                elementName);
252                        final Element arrayTypeElement =
253                            Axis2ClientUtils.getElementByAttribute(
254                                definition,
255                                NAME,
256                                stripPrefix(arrayElement.getAttribute(TYPE)));
257                        final String arrayComponentName = Axis2ClientUtils.getAttributeValueFromChildElement(
258                                arrayTypeElement,
259                                NAME,
260                                0);
261                        for (int ctr = 0; ctr < Array.getLength(bean); ctr++)
262                        {
263                            omElement.addChild(
264                                getOMElement(
265                                    definition,
266                                    schema,
267                                    currentComponentElement,
268                                    Array.get(
269                                        bean,
270                                        ctr),
271                                    arrayComponentName,
272                                    factory,
273                                    namespaces,
274                                    typeMapper,
275                                    evaluatingBeans));
276                        }
277                    }
278                    else
279                    {
280                        final String attributeValue = omNamespace != null ?
281                            omNamespace.getPrefix() + NS_SEPARATOR + beanType.getSimpleName() : beanType.getSimpleName();
282                        // - add the xsi:type attribute for complex types
283                        omElement.addAttribute(TYPE, attributeValue, namespaces.get(XSI_PREFIX));
284                    }
285                    try
286                    {
287                        final java.util.Map<String, Object> properties = PropertyUtils.describe(bean);
288                        for (final String name : properties.keySet())
289                        {
290                            if (!CLASS.equals(name))
291                            {
292                                final Object value = properties.get(name);
293                                if (value != null)
294                                {
295                                    omElement.addChild(
296                                            getOMElement(
297                                                    definition,
298                                                    schema,
299                                                    currentComponentElement,
300                                                    value,
301                                                    name,
302                                                    factory,
303                                                    namespaces,
304                                                    typeMapper,
305                                                    evaluatingBeans));
306                                }
307                            }
308                        }
309                    }
310                    catch (final Throwable throwable)
311                    {
312                        throw new RuntimeException(throwable);
313                    }
314                }
315                evaluatingBeans.remove(bean);
316            }
317            return omElement;
318        }
319    
320        private static final String ELEMENT_FORM_DEFAULT = "elementFormDefault";
321    
322        /**
323         * Indicates whether or not a xml document is qualified.
324         */
325        private static final String QUALIFIED = "qualified";
326    
327        /**
328         * The attribute that stores the target namespace.
329         */
330        private static final String TARGET_NAMESPACE = "targetNamespace";
331    
332        /**
333         * The schema instance namespace.
334         */
335        private static final String XSI_NS = "http://www.w3.org/2001/XMLSchema-instance";
336    
337        /**
338         * The prefix for the schema instance namespace.
339         */
340        private static final String XSI_PREFIX = "xsi";
341    
342        /**
343         * Used to separate a namespace prefix and name in a QName.
344         */
345        private static final String NS_SEPARATOR = ":";
346    
347        /**
348         * Indicates whether or not the schema is qualified.
349         *
350         * @param schema the schema to check.
351         * @return true/false
352         */
353        private static boolean isQualified(Schema schema)
354        {
355            boolean isQualified = false;
356            if (schema != null)
357            {
358                final String qualified = Axis2ClientUtils.getAttributeValue(
359                    schema.getElement(),
360                    ELEMENT_FORM_DEFAULT);
361                isQualified = QUALIFIED.equalsIgnoreCase(qualified);
362            }
363            return isQualified;
364        }
365    
366        private static String getTargetNamespace(Schema schema)
367        {
368            return Axis2ClientUtils.getAttributeValue(
369                schema.getElement(),
370                TARGET_NAMESPACE);
371        }
372    
373        private static String stripPrefix(String name)
374        {
375            return name.replaceAll(
376                ".*:",
377                "");
378        }
379    
380        private static String getNamespacePrefix(
381            final Definition definition,
382            final String namespace)
383        {
384            String prefix = null;
385            final Map<String, String> namespaces = definition.getNamespaces();
386            for (Map.Entry<String, String> entry : namespaces.entrySet())
387            {
388                if (entry.getValue().equals(namespace))
389                {
390                    prefix = entry.getKey();
391                }
392            }
393            return prefix;
394        }
395    
396        private static final String NAME = "name";
397        private static final String TYPE = "type";
398        private static final String BASE = "base";
399    
400        private static Element getRequiredElementByAttribute(
401            final Definition definition,
402            final Element container,
403            final String attribute,
404            final String value)
405        {
406            Element element = null;
407            if (container != null)
408            {
409                element = Axis2ClientUtils.getElementByAttribute(
410                    container,
411                    attribute,
412                    value);
413            }
414            // - try finding the element on an inherited type
415            if (element == null)
416            {
417                final String xsiTypeName = Axis2ClientUtils.getAttributeValueFromChildElement(container, BASE, 0);
418                final String typeName = getLocalName(xsiTypeName);
419                element = Axis2ClientUtils.getElementByAttribute(
420                    Axis2ClientUtils.getElementByAttribute(definition, NAME, typeName),
421                    attribute,
422                    value);
423            }
424            if (element == null)
425            {
426                String attr = container==null ? "" : container.getAttribute(NAME);
427                throw new RuntimeException('\'' + value + "' was not found on element '" + attr + '\'');
428            }
429            return element;
430        }
431    
432        private static Element getElementByAttribute(
433            final Definition definition,
434            final String attribute,
435            final String value)
436        {
437            Element element = null;
438            if (value != null)
439            {
440                final Types types = definition.getTypes();
441                final Collection elements = types.getExtensibilityElements();
442                for (final Iterator iterator = elements.iterator(); iterator.hasNext();)
443                {
444                    final Object object = iterator.next();
445                    if (object instanceof Schema)
446                    {
447                        final Schema schema = (Schema)object;
448                        element =
449                            getElementByAttribute(
450                                schema.getElement(),
451                                attribute,
452                                value);
453                        if (element != null)
454                        {
455                            break;
456                        }
457                    }
458                }
459            }
460            return element;
461        }
462    
463        private static Collection<Schema> getSchemas(final Definition definition)
464        {
465            final Collection<Schema> schemas = new ArrayList<Schema>();
466            final Types types = definition.getTypes();
467            final Collection elements = types.getExtensibilityElements();
468            for (final Iterator iterator = elements.iterator(); iterator.hasNext();)
469            {
470                final Object object = iterator.next();
471                if (object instanceof Schema)
472                {
473                    schemas.add((Schema)object);
474                }
475            }
476            return schemas;
477        }
478    
479        private static Element getElementByAttribute(
480            final Schema schema,
481            final String attribute,
482            final String value)
483        {
484            return getElementByAttribute(
485                schema.getElement(),
486                attribute,
487                value);
488        }
489    
490        /**
491         * Gets the schema that owns the element that has the given attribute with the given value.
492         *
493         * @param definition the WSDL definition.
494         * @param attribute the name of the attribute to find.
495         * @param value the value of the attribute.
496         * @return the schema
497         */
498        private static Schema getElementSchemaByAttribute(
499            final Definition definition,
500            final String attribute,
501            final String value)
502        {
503            final Types types = definition.getTypes();
504            final Collection elements = types.getExtensibilityElements();
505            Schema schema = null;
506            for (final Iterator iterator = elements.iterator(); iterator.hasNext();)
507            {
508                final Object object = iterator.next();
509                if (object instanceof Schema)
510                {
511                    final Element element = getElementByAttribute(
512                            (Schema)object,
513                            attribute,
514                            value);
515                    if (element != null)
516                    {
517                        schema = (Schema)object;
518                        break;
519                    }
520                }
521            }
522            return schema;
523        }
524    
525        private static Element getElementByAttribute(
526            final Element element,
527            final String attribute,
528            final String value)
529        {
530            Element found = null;
531            if (element != null && value != null)
532            {
533                final String foundAttribute = element.getAttribute(attribute);
534                if (StringUtils.isNotBlank(value) && value.equals(foundAttribute))
535                {
536                    found = element;
537                }
538                else
539                {
540                    final NodeList nodes = element.getChildNodes();
541                    for (int ctr = 0; ctr < nodes.getLength(); ctr++)
542                    {
543                        final Node node = nodes.item(ctr);
544                        if (node instanceof Element)
545                        {
546                            found =
547                                getElementByAttribute(
548                                    (Element)node,
549                                    attribute,
550                                    value);
551                            if (found != null)
552                            {
553                                break;
554                            }
555                        }
556                    }
557                }
558            }
559            return found;
560        }
561    
562        private static List<Element> getElementsWithAttribute(
563            final Element element,
564            final String attribute)
565        {
566            final List<Element> found = new ArrayList<Element>();
567            if (element != null)
568            {
569                final String foundAttribute = element.getAttribute(attribute);
570                if (StringUtils.isNotBlank(foundAttribute))
571                {
572                    found.add(element);
573                }
574                final NodeList nodes = element.getChildNodes();
575                for (int ctr = 0; ctr < nodes.getLength(); ctr++)
576                {
577                    final Node node = nodes.item(ctr);
578                    if (node instanceof Element)
579                    {
580                        found.addAll(getElementsWithAttribute(
581                                (Element)node,
582                                attribute));
583                    }
584                }
585            }
586            return found;
587        }
588    
589        private static String getAttributeValue(
590            final Element element,
591            final String attribute)
592        {
593            String value = null;
594            Element found = getElementWithAttribute(
595                    element,
596                    attribute);
597            if (found != null)
598            {
599                value = found.getAttribute(attribute);
600            }
601            return value;
602        }
603    
604        private static String getAttributeValueFromChildElement(
605            final Element element,
606            final String attribute,
607            int childIndex)
608        {
609            String value = null;
610            if (element != null)
611            {
612                int elementIndex = 0;
613                for (int ctr = 0; ctr < element.getChildNodes().getLength(); ctr++)
614                {
615                    final Node node = element.getChildNodes().item(ctr);
616                    if (node instanceof Element)
617                    {
618                        if (elementIndex == childIndex)
619                        {
620                            value =
621                                getAttributeValue(
622                                    (Element)node,
623                                    attribute);
624                        }
625                        elementIndex++;
626                    }
627                }
628            }
629            return value;
630        }
631    
632        /**
633         * Finds the first child element of the given <code>element</code> that has an attribute
634         * matching the name of <code>attribute</code>.
635         *
636         * @param element the element to search.
637         * @param attribute the name of the attribute to get.
638         * @return the found element or null if not found.
639         */
640        private static Element getElementWithAttribute(
641            final Element element,
642            final String attribute)
643        {
644            Element found = null;
645            if (element != null)
646            {
647                final NodeList nodes = element.getChildNodes();
648                final String foundAttribute = element.getAttribute(attribute);
649                if (StringUtils.isNotBlank(foundAttribute))
650                {
651                    found = element;
652                }
653                if (found == null)
654                {
655                    for (int ctr = 0; ctr < nodes.getLength(); ctr++)
656                    {
657                        final Node node = nodes.item(ctr);
658                        if (node instanceof Element)
659                        {
660                            found =
661                                getElementWithAttribute(
662                                    (Element)node,
663                                    attribute);
664                        }
665                    }
666                }
667            }
668            return found;
669        }
670    
671        private static final String CLASS = "class";
672    
673        /**
674         * Deserializes the given <code>element</code> to the given <code>type</code>.
675         *
676         * @param element the XML OMElement
677         * @param type the java type.
678         * @param typeMapper the "object creator" used to construct objects from given classes.
679         * @return the deserialized object.
680         * @throws Exception
681         */
682        public static Object deserialize(
683            OMElement element,
684            Class type,
685            final TypeMapper typeMapper) throws Exception
686        {
687            Object bean = null;
688            if (typeMapper.isSimpleType(type))
689            {
690                bean = getSimpleTypeObject(type, element, typeMapper);
691            }
692            else if (type == byte[].class)
693            {
694                bean = Base64.decode(element.getText());
695            }
696            else
697            {
698                if (type.isArray())
699                {
700                    final Collection<Object> elements = new ArrayList<Object>();
701                    for (final Iterator iterator = element.getChildElements(); iterator.hasNext();)
702                    {
703                        OMElement omElement = (OMElement)iterator.next();
704                        elements.add(deserialize(
705                                omElement,
706                                type.getComponentType(),
707                                typeMapper));
708                    }
709                    bean =
710                        elements.toArray((Object[])Array.newInstance(
711                                type.getComponentType(),
712                                0));
713                }
714                else
715                {
716                    try
717                    {
718                        type = getAppropriateType(element, type);
719                        bean = typeMapper.getObject(type);
720                        final java.beans.PropertyDescriptor[] descriptors = PropertyUtils.getPropertyDescriptors(type);
721                        for (int ctr = 0; ctr < descriptors.length; ctr++)
722                        {
723                            final java.beans.PropertyDescriptor descriptor = descriptors[ctr];
724                            final String name = descriptor.getName();
725                            if (!CLASS.equals(name))
726                            {
727                                final OMElement propertyElement = findElementByName(
728                                        element,
729                                        name);
730    
731                                if (propertyElement != null)
732                                {
733                                    PropertyUtils.setProperty(
734                                        bean,
735                                        name,
736                                        deserialize(
737                                            propertyElement,
738                                            descriptor.getPropertyType(),
739                                            typeMapper));
740                                }
741                            }
742                        }
743                    }
744                    catch (final Throwable throwable)
745                    {
746                        throw new RuntimeException(throwable);
747                    }
748                }
749            }
750            return bean;
751        }
752    
753        /**
754         * Gets the object given the type, element and typeMapper.
755         * @param type the type of object to get.
756         * @param element the elemtn that has the value to populate the object with.
757         * @param typeMapper the mapper used for retriving the value if it can't be found
758         *        by the simple type mapper.
759         * @return the object
760         */
761        private static Object getSimpleTypeObject(Class type,
762            OMElement element, TypeMapper typeMapper)
763        {
764            Object object = org.apache.axis2.databinding.typemapping.SimpleTypeMapper.getSimpleTypeObject(
765                type,
766                element);
767            if (object == null && element != null)
768            {
769                object = typeMapper.getObject(type, element.getText());
770            }
771            return object;
772        }
773    
774        /**
775         * The java package separator character.
776         */
777        private static final String PACKAGE_SEPARATOR = ".";
778    
779        /**
780         * The xsi:type Qname.
781         */
782        private static final QName XSI_TYPE_QNAME = new QName(XSI_NS, TYPE);
783    
784        /**
785         * Gets the appropriate type from checking the xsi:type (if present).  Currently
786         * this just assumes any types in a hierarchy are in the same package.
787         *
788         * @param element the element from which to retrieve the type.
789         * @param type the current type.
790         * @return the appropriate type.
791         * @throws ClassNotFoundException
792         */
793        private static Class getAppropriateType(final OMElement element, Class type) throws ClassNotFoundException
794        {
795            final String xsiTypeName = element.getAttributeValue(XSI_TYPE_QNAME);
796            if (xsiTypeName != null)
797            {
798                final String typeName = getLocalName(xsiTypeName);
799                if (!typeName.equals(type.getSimpleName()))
800                {
801                    // TODO: need to handle types that aren't in the same package (we should look up the
802                    // mapped class here instead of assuming the same package)
803                    type = Thread.currentThread().getContextClassLoader().loadClass(
804                        type.getPackage().getName() + PACKAGE_SEPARATOR + typeName);
805                }
806            }
807            return type;
808        }
809    
810        /**
811         * Strips the prefix from a type name in the given form: prefix:localName
812         * to get the local name.
813         *
814         * @param typeName the type name with an optional prefix
815         * @return the local name.
816         */
817        private static String getLocalName(final String typeName)
818        {
819            String localName;
820            String[] names = typeName.split(NS_SEPARATOR);
821            if (names.length > 1)
822            {
823                localName = names[1];
824            }
825            else
826            {
827                localName = names[0];
828            }
829            return localName;
830        }
831    
832        /**
833         * Finds an element having the given <code>name</code> from the child elements on the given
834         * <code>element</code>.
835         *
836         * @param element the element to search.
837         * @param name the name of the element to find.
838         * @return the found element or null if one couldn't be found.
839         */
840        private static OMElement findElementByName(
841            final OMElement element,
842            final String name)
843        {
844            OMElement found = null;
845            for (final Iterator iterator = element.getChildElements(); iterator.hasNext();)
846            {
847                final OMElement child = (OMElement)iterator.next();
848                if (child.getLocalName().equals(name))
849                {
850                    found = child;
851                    break;
852                }
853            }
854            return found;
855        }
856    
857        /**
858         * First delegate to the Axis2 simple type mapper, if that says
859         * its simple, it is, otherwise check to see if the type is an enumeration (typesafe
860         * or Java5 version).
861         *
862         * @param bean the bean to check.
863         * @return true/false
864         */
865        private static boolean isSimpleType(final Object bean, final TypeMapper typeMapper)
866        {
867            return typeMapper.isSimpleType(bean != null ? bean.getClass() : null);
868        }
869    }