class JAXBEncoderDecoder {

    public static void marshallException(Marshaller marshaller, Exception elValue,
                                         MessagePartInfo part, Object source) {
        XMLStreamWriter writer = getStreamWriter(source);
        QName qn = part.getElementQName();
        try {
            writer.writeStartElement("ns1", qn.getLocalPart(), qn.getNamespaceURI());
            Class<?> cls = part.getTypeClass();
            XmlAccessType accessType = Utils.getXmlAccessType(cls);
            String namespace = part.getElementQName().getNamespaceURI();
            String attNs = namespace;

            SchemaInfo sch = part.getMessageInfo().getOperation().getInterface()
                .getService().getSchema(namespace);
            if (sch == null) {
                LOG.warning("Schema associated with " + namespace + " is null");
                namespace = null;
                attNs = null;
            } else {
                if (!sch.isElementFormQualified()) {
                    namespace = null;
                }
                if (!sch.isAttributeFormQualified()) {
                    attNs = null;
                }
            }
            List<Member> combinedMembers = new ArrayList<>();

            for (Field f : Utils.getFields(cls, accessType)) {
                XmlAttribute at = f.getAnnotation(XmlAttribute.class);
                if (at == null) {
                    combinedMembers.add(f);
                } else {
                    QName fname = new QName(attNs, StringUtils.isEmpty(at.name()) ? f.getName() : at.name());
                    ReflectionUtil.setAccessible(f);
                    Object o = Utils.getFieldValue(f, elValue);
                    DocumentFragment frag = DOMUtils.getEmptyDocument().createDocumentFragment();
                    writeObject(marshaller, frag, newJAXBElement(fname, String.class, o));

                    if (attNs != null) {
                        writer.writeAttribute(attNs, fname.getLocalPart(),
                                              DOMUtils.getAllContent(frag));
                    } else {
                        writer.writeAttribute(fname.getLocalPart(), DOMUtils.getAllContent(frag));
                    }
                }
            }
            for (Method m : Utils.getGetters(cls, accessType)) {
                if (!m.isAnnotationPresent(XmlAttribute.class)) {
                    combinedMembers.add(m);
                } else {
                    int idx = m.getName().startsWith("get") ? 3 : 2;
                    String name = m.getName().substring(idx);
                    name = Character.toLowerCase(name.charAt(0)) + name.substring(1);
                    XmlAttribute at = m.getAnnotation(XmlAttribute.class);
                    QName mname = new QName(namespace, StringUtils.isEmpty(at.name()) ? name : at.name());
                    DocumentFragment frag = DOMUtils.getEmptyDocument().createDocumentFragment();
                    Object o = Utils.getMethodValue(m, elValue);
                    writeObject(marshaller, frag, newJAXBElement(mname, String.class, o));
                    if (attNs != null) {
                        writer.writeAttribute(attNs, mname.getLocalPart(),
                                              DOMUtils.getAllContent(frag));
                    } else {
                        writer.writeAttribute(mname.getLocalPart(), DOMUtils.getAllContent(frag));
                    }
                }
            }

            XmlAccessorOrder xmlAccessorOrder = cls.getAnnotation(XmlAccessorOrder.class);
            if (xmlAccessorOrder != null && xmlAccessorOrder.value().equals(XmlAccessOrder.ALPHABETICAL)) {
                Collections.sort(combinedMembers, new Comparator<Member>() {
                    public int compare(Member m1, Member m2) {
                        return m1.getName().compareTo(m2.getName());
                    }
                });
            }
            XmlType xmlType = cls.getAnnotation(XmlType.class);
            if (xmlType != null && xmlType.propOrder().length > 1 && !xmlType.propOrder()[0].isEmpty()) {
                final List<String> orderList = Arrays.asList(xmlType.propOrder());
                Collections.sort(combinedMembers, new Comparator<Member>() {
                    public int compare(Member m1, Member m2) {
                        String m1Name = getName(m1);
                        String m2Name = getName(m2);
                        int m1Index = orderList.indexOf(m1Name);
                        int m2Index = orderList.indexOf(m2Name);
                        if (m1Index != -1 && m2Index != -1) {
                            return m1Index - m2Index;
                        }
                        if (m1Index == -1 && m2Index != -1) {
                            return 1;
                        }
                        if (m1Index != -1 && m2Index == -1) {
                            return -1;
                        }
                        return 0;
                    }
                });
            }
            for (Member member : combinedMembers) {
                if (member instanceof Field) {
                    Field f = (Field)member;
                    QName fname = new QName(namespace, f.getName());
                    ReflectionUtil.setAccessible(f);
                    if (JAXBSchemaInitializer.isArray(f.getGenericType())) {
                        writeArrayObject(marshaller, writer, fname, f.get(elValue));
                    } else {
                        Object o = Utils.getFieldValue(f, elValue);
                        writeObject(marshaller, writer, newJAXBElement(fname, String.class, o));
                    }
                } else { // it's a Method
                    Method m = (Method)member;
                    int idx = m.getName().startsWith("get") ? 3 : 2;
                    String name = m.getName().substring(idx);
                    name = Character.toLowerCase(name.charAt(0)) + name.substring(1);
                    QName mname = new QName(namespace, name);
                    if (JAXBSchemaInitializer.isArray(m.getGenericReturnType())) {
                        writeArrayObject(marshaller, writer, mname, m.invoke(elValue));
                    } else {
                        Object o = Utils.getMethodValue(m, elValue);
                        writeObject(marshaller, writer, newJAXBElement(mname, String.class, o));
                    }
                }
            }

            writer.writeEndElement();
            writer.flush();
        } catch (Exception e) {
            throw new Fault(new Message("MARSHAL_ERROR", LOG, e.getMessage()), e);
        } finally {
            StaxUtils.close(writer);
        }
    }

    private  JAXBEncoderDecoder();

}