28
28
import org .exist .storage .serializers .EXistOutputKeys ;
29
29
import org .exist .util .serializer .XQuerySerializer ;
30
30
import org .exist .xquery .*;
31
+ import org .exist .xquery .functions .array .ArrayType ;
31
32
import org .exist .xquery .functions .map .AbstractMapType ;
32
33
import org .exist .xquery .util .SerializerUtils ;
33
34
import org .exist .xquery .value .*;
@@ -82,13 +83,16 @@ public Sequence eval(Sequence[] args, Sequence contextSequence) throws XPathExce
82
83
try (final StringWriter writer = new StringWriter ()) {
83
84
final XQuerySerializer xqSerializer = new XQuerySerializer (context .getBroker (), outputProperties , writer );
84
85
85
- Sequence seq = args [0 ];
86
+ final Sequence input = args [0 ];
86
87
if (xqSerializer .normalize ()) {
87
88
final String itemSeparator = outputProperties .getProperty (EXistOutputKeys .ITEM_SEPARATOR , DEFAULT_ITEM_SEPARATOR );
88
- seq = normalize (this , context , seq , itemSeparator );
89
+ final Sequence normalized = normalize (this , context , input , itemSeparator );
90
+ xqSerializer .serialize (normalized );
91
+ }
92
+ else {
93
+ xqSerializer .serialize (input );
89
94
}
90
95
91
- xqSerializer .serialize (seq );
92
96
return new StringValue (this , writer .toString ());
93
97
} catch (final IOException | SAXException e ) {
94
98
throw new XPathException (this , FnModule .SENR0001 , e .getMessage ());
@@ -97,9 +101,9 @@ public Sequence eval(Sequence[] args, Sequence contextSequence) throws XPathExce
97
101
98
102
public static Properties getSerializationProperties (final Expression callingExpr , final Item parametersItem ) throws XPathException {
99
103
final Properties outputProperties ;
100
- if (parametersItem .getType () == Type .MAP ) {
104
+ if (parametersItem .getType () == Type .MAP ) {
101
105
outputProperties = SerializerUtils .getSerializationOptions (callingExpr , (AbstractMapType ) parametersItem );
102
- } else if (isSerializationParametersElement (parametersItem )) {
106
+ } else if (isSerializationParametersElement (parametersItem )) {
103
107
outputProperties = new Properties ();
104
108
SerializerUtils .getSerializationOptions (callingExpr , (NodeValue ) parametersItem , outputProperties );
105
109
} else {
@@ -141,48 +145,59 @@ private static boolean isSerializationParametersElement(final Item item) {
141
145
* @throws XPathException in case of dynamic error.
142
146
*/
143
147
public static Sequence normalize (final Expression callingExpr , final XQueryContext context , final Sequence input , final String itemSeparator ) throws XPathException {
144
- if (input .isEmpty ())
145
- // "If the sequence that is input to serialization is empty, create a sequence S1 that consists of a zero-length string."
146
- {return StringValue .EMPTY_STRING ;}
147
- final ValueSequence temp = new ValueSequence (input .getItemCount ());
148
+ // "If the sequence that is input to serialization is empty, create a sequence S1 that consists of a zero-length string."
149
+ if (input .isEmpty ()) {
150
+ return StringValue .EMPTY_STRING ;
151
+ }
152
+ // flatten arrays
153
+ final ValueSequence step1 = new ValueSequence ();
148
154
for (final SequenceIterator i = input .iterate (); i .hasNext (); ) {
149
155
final Item next = i .nextItem ();
150
- if (Type .subTypeOf (next .getType (), Type .NODE )) {
151
- if (next .getType () == Type .ATTRIBUTE || next .getType () == Type .NAMESPACE || next .getType () == Type .FUNCTION_REFERENCE )
152
- {throw new XPathException (callingExpr , FnModule .SENR0001 ,
153
- "It is an error if an item in the sequence to serialize is an attribute node or a namespace node." );}
154
- if (itemSeparator != null && itemSeparator .length () > 0 && !temp .isEmpty ()) {
155
- temp .add (new StringValue (itemSeparator + next .getStringValue ()));
156
- } else {
157
- temp .add (next );
156
+ if (next .getType () == Type .ARRAY ) {
157
+ final Sequence sequence = ArrayType .flatten (next );
158
+ for (final SequenceIterator si = sequence .iterate (); si .hasNext (); ) {
159
+ step1 .add (si .nextItem ());
160
+ }
161
+ } else {
162
+ step1 .add (next );
163
+ }
164
+ }
165
+
166
+ final ValueSequence step2 = new ValueSequence (step1 .getItemCount ());
167
+ for (final SequenceIterator i = step1 .iterate (); i .hasNext (); ) {
168
+ final Item next = i .nextItem ();
169
+ final int itemType = next .getType ();
170
+ if (Type .subTypeOf (itemType , Type .NODE )) {
171
+ if (itemType == Type .ATTRIBUTE || itemType == Type .NAMESPACE || itemType == Type .FUNCTION_REFERENCE ) {
172
+ throw new XPathException (callingExpr , FnModule .SENR0001 ,
173
+ "It is an error if an item in the sequence to serialize is an attribute node or a namespace node." );
158
174
}
175
+ step2 .add (next );
159
176
} else {
160
177
// atomic value
161
- Item last = null ;
162
- if (!temp .isEmpty ())
163
- {last = temp .itemAt (temp .getItemCount () - 1 );}
164
- if (last != null && last .getType () == Type .STRING )
165
- // "For each subsequence of adjacent strings in S2, copy a single string to the new sequence
166
- // equal to the values of the strings in the subsequence concatenated in order, each separated
167
- // by a single space."
168
- {((StringValue )last ).append ((itemSeparator == null ? " " : itemSeparator ) + next .getStringValue ());}
169
- else
170
- // "For each item in S1, if the item is atomic, obtain the lexical representation of the item by
171
- // casting it to an xs:string and copy the string representation to the new sequence;"
172
- {temp .add (new StringValue (callingExpr , next .getStringValue ()));}
178
+ // "For each item in S1, if the item is atomic, obtain the lexical representation of the item by
179
+ // casting it to an xs:string and copy the string representation to the new sequence;"
180
+ final StringValue stringRepresentation = new StringValue (callingExpr , next .getStringValue ());
181
+ step2 .add (stringRepresentation );
173
182
}
174
183
}
175
184
176
185
context .pushDocumentContext ();
177
186
try {
178
187
final MemTreeBuilder builder = context .getDocumentBuilder ();
179
188
final DocumentBuilderReceiver receiver = new DocumentBuilderReceiver (callingExpr , builder , true );
180
- for (final SequenceIterator i = temp .iterate (); i .hasNext (); ) {
189
+ final String safeItemSeparator = itemSeparator == null ? "" : itemSeparator ;
190
+ for (final SequenceIterator i = step2 .iterate (); i .hasNext (); ) {
181
191
final Item next = i .nextItem ();
182
192
if (Type .subTypeOf (next .getType (), Type .NODE )) {
183
193
next .copyTo (context .getBroker (), receiver );
184
194
} else {
185
- receiver .characters (next .getStringValue ());
195
+ final String stringValue = next .getStringValue ();
196
+ receiver .characters (stringValue );
197
+ }
198
+ // add itemSeparator if there is a next item
199
+ if (i .hasNext ()) {
200
+ receiver .characters (safeItemSeparator );
186
201
}
187
202
}
188
203
return (DocumentImpl )receiver .getDocument ();
0 commit comments