• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

evolvedbinary / elemental / 982

29 Apr 2025 08:34PM UTC coverage: 56.409% (+0.007%) from 56.402%
982

push

circleci

adamretter
[feature] Improve README.md badges

28451 of 55847 branches covered (50.94%)

Branch coverage included in aggregate %.

77468 of 131924 relevant lines covered (58.72%)

0.59 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

68.99
/exist-core/src/main/java/org/exist/xquery/functions/fn/transform/Convert.java
1
/*
2
 * Elemental
3
 * Copyright (C) 2024, Evolved Binary Ltd
4
 *
5
 * admin@evolvedbinary.com
6
 * https://www.evolvedbinary.com | https://www.elemental.xyz
7
 *
8
 * Use of this software is governed by the Business Source License 1.1
9
 * included in the LICENSE file and at www.mariadb.com/bsl11.
10
 *
11
 * Change Date: 2028-04-27
12
 *
13
 * On the date above, in accordance with the Business Source License, use
14
 * of this software will be governed by the Apache License, Version 2.0.
15
 *
16
 * Additional Use Grant: Production use of the Licensed Work for a permitted
17
 * purpose. A Permitted Purpose is any purpose other than a Competing Use.
18
 * A Competing Use means making the Software available to others in a commercial
19
 * product or service that: substitutes for the Software; substitutes for any
20
 * other product or service we offer using the Software that exists as of the
21
 * date we make the Software available; or offers the same or substantially
22
 * similar functionality as the Software.
23
 */
24
package org.exist.xquery.functions.fn.transform;
25

26
import it.unimi.dsi.fastutil.ints.IntList;
27
import net.sf.saxon.s9api.*;
28
import net.sf.saxon.type.BuiltInAtomicType;
29
import org.exist.dom.QName;
30
import org.exist.dom.persistent.NodeProxy;
31
import org.exist.xquery.ErrorCodes;
32
import org.exist.xquery.XPathException;
33
import org.exist.xquery.functions.array.ArrayType;
34
import org.exist.xquery.functions.fn.FnTransform;
35
import org.exist.xquery.value.*;
36
import org.w3c.dom.Document;
37
import org.w3c.dom.Node;
38

39
import javax.xml.XMLConstants;
40
import javax.xml.transform.dom.DOMSource;
41
import java.util.ArrayList;
42
import java.util.Iterator;
43
import java.util.List;
44

45
/**
46
 * Type conversion to and from Saxon
47
 *
48
 * <p>
49
 *     Used to convert values to/from Saxon when we use Saxon as the XSLT transformer
50
 *     as in the fn:transform implementation {@link FnTransform}
51
 *     A very minimal set of conversions where they are absolutely needed.
52
 *     Most conversion is carried out via a document, but that is insufficient in a few cases
53
 *     (where the delivery-format is raw and a template has a specified output type
54
 *     e.g. <xsl:template name='main' as='xs:integer'></xsl:template>
55
 *
56
 *     The correct path would be to make this conversion comprehensive and use it in all cases.
57
 *     It's not clear how easy or hard that would be.
58
 * </p>
59
 */
60
class Convert {
61

62
    private Convert() {
63
        super();
64
    }
65

66
    static class ToExist {
67

68
        private ToExist() { super(); }
69

70
        static Sequence of(final XdmValue xdmValue) throws XPathException {
71
            if (xdmValue.size() == 0) {
1✔
72
                return Sequence.EMPTY_SEQUENCE;
1✔
73
            }
74

75
            final ValueSequence valueSequence = new ValueSequence();
1✔
76
            for (final XdmItem xdmItem : xdmValue) {
1✔
77

78
                valueSequence.add(ToExist.ofItem(xdmItem));
1✔
79
            }
80
            return valueSequence;
1✔
81
        }
82

83
        static Item ofItem(final XdmItem xdmItem) throws XPathException {
84

85
            if (xdmItem.isAtomicValue()) {
1!
86
                final net.sf.saxon.value.AtomicValue atomicValue = (net.sf.saxon.value.AtomicValue) xdmItem.getUnderlyingValue();
1✔
87
                final BuiltInAtomicType atomicType = atomicValue.getPrimitiveType();
1✔
88
                if (atomicType == BuiltInAtomicType.INTEGER) {
1✔
89
                    return new IntegerValue(atomicValue.getStringValue());
1✔
90
                } else if (atomicType == BuiltInAtomicType.DOUBLE) {
1!
91
                    return new DoubleValue(atomicValue.getStringValue());
1✔
92
                } else {
93
                    throw new XPathException(ErrorCodes.XPTY0004,
×
94
                            "net.sf.saxon.value.AtomicValue " + atomicValue + COULD_NOT_BE_CONVERTED + "atomic value");
×
95
                }
96
            } else if (xdmItem instanceof XdmNode) {
×
97
                return ToExist.ofNode((XdmNode)xdmItem);
×
98
            }
99

100
            throw new XPathException(ErrorCodes.XPTY0004,
×
101
                    "XdmItem " + xdmItem + COULD_NOT_BE_CONVERTED + "Sequence");
×
102
        }
103

104
        static NodeValue ofNode(final XdmNode xdmNode) throws XPathException {
105

106
            throw new XPathException(ErrorCodes.XPTY0004,
×
107
                    "XdmNode " + xdmNode + COULD_NOT_BE_CONVERTED + " Node");
×
108
        }
109
    }
110

111
    static final private String COULD_NOT_BE_CONVERTED = " could not be converted to an eXist ";
112

113
    abstract static class ToSaxon {
1✔
114

115
        abstract DocumentBuilder newDocumentBuilder();
116

117
        static net.sf.saxon.s9api.QName of(final QName qName) {
118
            return new net.sf.saxon.s9api.QName(qName.getPrefix() == null ? "" : qName.getPrefix(), qName.getNamespaceURI(), qName.getLocalPart());
1!
119
        }
120

121
        static net.sf.saxon.s9api.QName of(final QNameValue qName) {
122
            return of(qName.getQName());
1✔
123
        }
124

125
        XdmValue of(final Item item) throws XPathException {
126
            if (item instanceof NodeProxy nodeProxy) {
1✔
127
                return ofNode(nodeProxy.getNode());
1✔
128
            }
129
            final int itemType = item.getType();
1✔
130
            if (Type.subTypeOf(itemType, Type.ANY_ATOMIC_TYPE)) {
1✔
131
                return ofAtomic((AtomicValue) item);
1✔
132
            } else if (Type.subTypeOf(itemType, Type.NODE)) {
1!
133
                if (item instanceof NodeProxy) {
1!
134
                    return ofNode(((NodeProxy) item).getNode());
×
135
                } else {
136
                    return ofNode((Node) item);
1✔
137
                }
138
            }
139
            throw new XPathException(ErrorCodes.XPTY0004,
×
140
                    "Item " + item + " of type " + Type.getTypeName(itemType) + COULD_NOT_BE_CONVERTED + "XdmValue");
×
141
        }
142

143
        static private XdmValue ofAtomic(final AtomicValue atomicValue) throws XPathException {
144
            final int itemType = atomicValue.getType();
1✔
145
            if (Type.subTypeOf(itemType, Type.INTEGER)) {
1✔
146
                return XdmValue.makeValue(((IntegerValue) atomicValue).getInt());
1✔
147
            } else if (Type.subTypeOf(itemType, Type.NUMERIC)) {
1!
148
                return XdmValue.makeValue(((NumericValue) atomicValue).getDouble());
×
149
            } else if (Type.subTypeOf(itemType, Type.BOOLEAN)) {
1!
150
                return XdmValue.makeValue(((BooleanValue) atomicValue).getValue());
×
151
            } else if (Type.subTypeOf(itemType, Type.STRING)) {
1!
152
                return XdmValue.makeValue(((StringValue) atomicValue).getStringValue());
1✔
153
            }
154

155
            throw new XPathException(ErrorCodes.XPTY0004,
×
156
                    "Atomic value " + atomicValue + " of type " + Type.getTypeName(itemType) +
×
157
                            COULD_NOT_BE_CONVERTED + "XdmValue");
158
        }
159

160
        private XdmValue ofNode(final Node node) throws XPathException {
161

162
            final DocumentBuilder sourceBuilder = newDocumentBuilder();
1✔
163
            try {
164
                if (node instanceof Document) {
1✔
165
                    return sourceBuilder.build(new DOMSource(node));
1✔
166

167
                } else {
168
                    //The source must be part of a document
169
                    final Document document = node.getOwnerDocument();
1✔
170
                    if (document == null) {
1!
171
                        throw new XPathException(ErrorCodes.XPTY0004, "Node " + node + COULD_NOT_BE_CONVERTED + "XdmValue, as it is not part of a document.");
×
172
                    }
173
                    final boolean implicitDocument = node instanceof org.exist.dom.memtree.NodeImpl && !((org.exist.dom.memtree.DocumentImpl) node.getOwnerDocument()).isExplicitlyCreated();
1✔
174
                    final IntList nodeIndex = TreeUtils.treeIndex(node, implicitDocument);
1✔
175
                    final XdmNode xdmDocument = sourceBuilder.build(new DOMSource(document));
1✔
176
                    final XdmNode xdmNode = TreeUtils.xdmNodeAtIndex(xdmDocument, nodeIndex);
1✔
177
                    if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
1✔
178
                        final net.sf.saxon.s9api.QName attrName = new net.sf.saxon.s9api.QName(node.getPrefix() == null ? XMLConstants.DEFAULT_NS_PREFIX : node.getPrefix(), node.getNamespaceURI(), node.getLocalName());
1!
179
                        final Iterator<XdmNode> itAttr = xdmNode.axisIterator(Axis.ATTRIBUTE, attrName);
1✔
180
                        if (itAttr.hasNext()) {
1!
181
                            return itAttr.next();
1✔
182
                        }
183
                        throw new XPathException(ErrorCodes.XPTY0004, "Node " + node + COULD_NOT_BE_CONVERTED + "XdmValue");
×
184

185
                    } else {
186
                        return xdmNode;
1✔
187
                    }
188
                }
189
            } catch (final SaxonApiException e) {
×
190
                throw new XPathException(ErrorCodes.XPTY0004, "Node " + node + COULD_NOT_BE_CONVERTED + "XdmValue", e);
×
191
            }
192
        }
193

194
        XdmValue[] of(final ArrayType values) throws XPathException {
195
            final int size = values.getSize();
×
196
            final XdmValue[] result = new XdmValue[size];
×
197
            for (int i = 0; i < size; i++) {
×
198
                final Sequence sequence = values.get(i);
×
199
                result[i] = XdmValue.makeValue(listOf(sequence));
×
200
            }
201
            return result;
×
202
        }
203

204
        XdmValue of(final Sequence value) throws XPathException {
205
            if (value instanceof NodeProxy nodeProxy) {
1✔
206
                return ofNode(nodeProxy.getNode());
1✔
207
            }
208
            return XdmValue.makeSequence(listOf(value));
1✔
209
        }
210

211
        private List<XdmValue> listOf(final Sequence value) throws XPathException {
212
            final int size = value.getItemCount();
1✔
213
            final List<XdmValue> result = new ArrayList<>(size);
1✔
214
            for (int i = 0; i < size; i++) {
1✔
215
                result.add(of(value.itemAt(i)));
1✔
216
            }
217
            return result;
1✔
218
        }
219
    }
220
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc