Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
View | Details | Raw Unified | Return to bug 411377 | Differences between
and this patch

Collapse All | Expand All

(-)a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/oxm/json/JsonArrayBuilderResult.java (-2 / +2 lines)
Lines 15-21 package org.eclipse.persistence.oxm.json; Link Here
15
import javax.json.Json;
15
import javax.json.Json;
16
import javax.json.JsonArrayBuilder;
16
import javax.json.JsonArrayBuilder;
17
import org.eclipse.persistence.internal.oxm.record.ExtendedResult;
17
import org.eclipse.persistence.internal.oxm.record.ExtendedResult;
18
import org.eclipse.persistence.oxm.record.JsonObjectBuilderWriterRecord;
18
import org.eclipse.persistence.oxm.record.JsonBuilderWriterRecord;
19
19
20
20
21
public class JsonArrayBuilderResult extends ExtendedResult{
21
public class JsonArrayBuilderResult extends ExtendedResult{
Lines 32-38 public class JsonArrayBuilderResult extends ExtendedResult{ Link Here
32
32
33
    @Override
33
    @Override
34
    public org.eclipse.persistence.oxm.record.MarshalRecord createRecord() {       
34
    public org.eclipse.persistence.oxm.record.MarshalRecord createRecord() {       
35
         return new JsonObjectBuilderWriterRecord(jsonArrayBuilder);
35
         return new JsonBuilderWriterRecord(jsonArrayBuilder);
36
    }
36
    }
37
37
38
    public JsonArrayBuilder getJsonArrayBuilder() {
38
    public JsonArrayBuilder getJsonArrayBuilder() {
(-)a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/oxm/json/JsonObjectBuilderResult.java (-2 / +2 lines)
Lines 15-21 package org.eclipse.persistence.oxm.json; Link Here
15
import javax.json.Json;
15
import javax.json.Json;
16
import javax.json.JsonObjectBuilder;
16
import javax.json.JsonObjectBuilder;
17
import org.eclipse.persistence.internal.oxm.record.ExtendedResult;
17
import org.eclipse.persistence.internal.oxm.record.ExtendedResult;
18
import org.eclipse.persistence.oxm.record.JsonObjectBuilderWriterRecord;
18
import org.eclipse.persistence.oxm.record.JsonBuilderWriterRecord;
19
19
20
20
21
public class JsonObjectBuilderResult extends ExtendedResult{
21
public class JsonObjectBuilderResult extends ExtendedResult{
Lines 32-38 public class JsonObjectBuilderResult extends ExtendedResult{ Link Here
32
32
33
    @Override
33
    @Override
34
    public org.eclipse.persistence.oxm.record.MarshalRecord createRecord() {       
34
    public org.eclipse.persistence.oxm.record.MarshalRecord createRecord() {       
35
         return new JsonObjectBuilderWriterRecord(jsonObjectBuilder);
35
         return new JsonBuilderWriterRecord(jsonObjectBuilder);
36
    }
36
    }
37
37
38
    public JsonObjectBuilder getJsonObjectBuilder() {
38
    public JsonObjectBuilder getJsonObjectBuilder() {
(-)a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/oxm/record/JsonBuilderWriterRecord.java (+813 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
3
 * This program and the accompanying materials are made available under the
4
 * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
5
 * which accompanies this distribution.
6
 * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
7
 * and the Eclipse Distribution License is available at
8
 * http://www.eclipse.org/org/documents/edl-v10.php.
9
 *
10
 * Contributors:
11
 *     Denise Smith - 2.6 - initial implementation
12
 ******************************************************************************/
13
package org.eclipse.persistence.oxm.record;
14
15
import java.io.IOException;
16
import java.io.StringWriter;
17
import java.math.BigDecimal;
18
import java.math.BigInteger;
19
import java.util.List;
20
21
import javax.json.Json;
22
import javax.json.JsonArrayBuilder;
23
import javax.json.JsonObjectBuilder;
24
import javax.xml.namespace.QName;
25
26
import org.eclipse.persistence.exceptions.XMLMarshalException;
27
import org.eclipse.persistence.internal.core.helper.CoreClassConstants;
28
import org.eclipse.persistence.internal.core.helper.CoreConversionManager;
29
import org.eclipse.persistence.internal.oxm.CharacterEscapeHandler;
30
import org.eclipse.persistence.internal.oxm.Constants;
31
import org.eclipse.persistence.internal.oxm.ConversionManager;
32
import org.eclipse.persistence.internal.oxm.NamespaceResolver;
33
import org.eclipse.persistence.internal.oxm.ObjectBuilder;
34
import org.eclipse.persistence.internal.oxm.Root;
35
import org.eclipse.persistence.internal.oxm.XMLBinaryDataHelper;
36
import org.eclipse.persistence.internal.oxm.XMLMarshaller;
37
import org.eclipse.persistence.internal.oxm.XPathFragment;
38
import org.eclipse.persistence.internal.oxm.XMLConversionManager;
39
import org.eclipse.persistence.internal.oxm.mappings.Descriptor;
40
import org.eclipse.persistence.internal.oxm.record.ExtendedContentHandler;
41
import org.eclipse.persistence.internal.oxm.record.XMLFragmentReader;
42
import org.w3c.dom.Attr;
43
import org.w3c.dom.Node;
44
import org.xml.sax.Attributes;
45
import org.xml.sax.Locator;
46
import org.xml.sax.SAXException;
47
import org.xml.sax.ext.LexicalHandler;
48
49
public class JsonBuilderWriterRecord extends MarshalRecord <XMLMarshaller> {
50
51
    private Level position;
52
    private JsonObjectBuilder rootJsonObjectBuilder;
53
    private JsonArrayBuilder rootJsonArrayBuilder;
54
    private CharacterEscapeHandler characterEscapeHandler;
55
56
    private String attributePrefix;
57
    private boolean isRootArray;
58
    private static final String NULL="null";
59
    private boolean isLastEventStart;
60
        
61
    public JsonBuilderWriterRecord(){
62
        super();
63
        isLastEventStart = false;
64
    }
65
    
66
    public JsonBuilderWriterRecord(JsonObjectBuilder jsonObjectBuilder){
67
        this();
68
        rootJsonObjectBuilder = jsonObjectBuilder;
69
    }
70
    
71
    public JsonBuilderWriterRecord(JsonArrayBuilder jsonArrayBuilder){
72
        this();
73
        rootJsonArrayBuilder = jsonArrayBuilder;
74
        isRootArray = true;
75
    }
76
    
77
    /**
78
     * INTERNAL:
79
     */
80
    public void setMarshaller(XMLMarshaller marshaller) {
81
        super.setMarshaller(marshaller);
82
        attributePrefix = marshaller.getAttributePrefix();
83
        if (marshaller.getValueWrapper() != null) {
84
            textWrapperFragment = new XPathFragment();
85
            textWrapperFragment.setLocalName(marshaller.getValueWrapper());
86
        }
87
        characterEscapeHandler = marshaller.getCharacterEscapeHandler();
88
    }
89
    
90
    @Override
91
    public void startDocument(String encoding, String version) {      
92
        if(isRootArray){
93
            if(position == null){
94
                startCollection();
95
            }
96
            position.setEmptyCollection(false);
97
            
98
            Level newLevel = new Level(false, position);
99
            position = newLevel;
100
            
101
            isLastEventStart = true;
102
        }else{
103
            Level rootLevel = new Level(false, null);
104
            position = rootLevel;
105
            if(rootJsonObjectBuilder == null){
106
                rootJsonObjectBuilder = Json.createObjectBuilder();
107
            }  
108
            
109
            rootLevel.setJsonObjectBuilder(rootJsonObjectBuilder);
110
        }
111
    }
112
113
    @Override
114
    public void endDocument() {
115
        if(position != null){
116
            if(position.parentLevel != null && position.parentLevel.isCollection){
117
                popAndSetInParentBuilder();
118
            }else{
119
                //this is the root level list case
120
                position = position.parentLevel;
121
            }
122
        }
123
    }
124
    
125
    private void popAndSetInParentBuilder(){
126
        Level removedLevel = position;
127
        Level parentLevel = position.parentLevel;
128
        position = position.parentLevel;
129
        if(removedLevel.isCollection && removedLevel.isEmptyCollection() && removedLevel.keyName == null){
130
            return;
131
        }
132
      
133
        if(parentLevel != null){                  
134
            if(parentLevel.isCollection){
135
                if(removedLevel.isCollection){
136
                    parentLevel.getJsonArrayBuilder().add(removedLevel.getJsonArrayBuilder());
137
                }else{                   
138
                    parentLevel.getJsonArrayBuilder().add(removedLevel.getJsonObjectBuilder());
139
                }
140
            }else{
141
                if(removedLevel.isCollection){                    
142
                    parentLevel.getJsonObjectBuilder().add(removedLevel.getKeyName(), removedLevel.getJsonArrayBuilder());
143
                }else{
144
                    parentLevel.getJsonObjectBuilder().add(removedLevel.getKeyName(), removedLevel.getJsonObjectBuilder());
145
                }
146
            }
147
        }
148
        
149
    }    
150
    
151
    public void startCollection() {
152
        if(position == null){
153
             isRootArray = true;              
154
             Level rootLevel = new Level(true, null);
155
             if(rootJsonArrayBuilder == null){
156
                  rootJsonArrayBuilder = Json.createArrayBuilder();
157
             }
158
             rootLevel.setJsonArrayBuilder(rootJsonArrayBuilder);
159
             position = rootLevel;
160
        } else {            
161
            if(isLastEventStart){
162
                position.setComplex(true);           
163
            }            
164
            Level level = new Level(true, position);            
165
            position = level;
166
        }      
167
        isLastEventStart = false;
168
    }
169
    
170
    @Override
171
    public void endCollection() {
172
         popAndSetInParentBuilder();    
173
    }
174
    
175
    @Override    
176
    public void openStartElement(XPathFragment xPathFragment, NamespaceResolver namespaceResolver) {
177
        super.openStartElement(xPathFragment, namespaceResolver);
178
        if(position != null){
179
            Level newLevel = new Level(false, position);            
180
            
181
            if(isLastEventStart){ 
182
                //this means 2 startevents in a row so the last this is a complex object
183
                position.setComplex(true);                                
184
            }
185
                      
186
            String keyName = getKeyName(xPathFragment);
187
           
188
            if(position.isCollection && position.isEmptyCollection() ){
189
                position.setKeyName(keyName);
190
            }else{
191
                newLevel.setKeyName(keyName);    
192
            }
193
            position = newLevel;   
194
            isLastEventStart = true;
195
        }
196
    }
197
    
198
    protected String getKeyName(XPathFragment xPathFragment){
199
        String keyName = xPathFragment.getLocalName(); 
200
       
201
        if(isNamespaceAware()){
202
            if(xPathFragment.getNamespaceURI() != null){
203
                String prefix = null;
204
                if(getNamespaceResolver() !=null){
205
                    prefix = getNamespaceResolver().resolveNamespaceURI(xPathFragment.getNamespaceURI());
206
                } else if(namespaceResolver != null){
207
                    prefix = namespaceResolver.resolveNamespaceURI(xPathFragment.getNamespaceURI());
208
                }
209
                if(prefix != null && !prefix.equals(Constants.EMPTY_STRING)){
210
                    keyName = prefix + getNamespaceSeparator() +  keyName;                           
211
                }
212
            }
213
        } 
214
        if(xPathFragment.isAttribute() && attributePrefix != null){
215
            keyName = attributePrefix + keyName;
216
        }
217
218
        return keyName;
219
    }
220
221
    public void attribute(XPathFragment xPathFragment, NamespaceResolver namespaceResolver,  Object value, QName schemaType){
222
        if(xPathFragment.getNamespaceURI() != null && xPathFragment.getNamespaceURI() == javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI){
223
            return;
224
        }
225
        xPathFragment.setAttribute(true);
226
        openStartElement(xPathFragment, namespaceResolver);
227
        characters(schemaType, value, null, false, true);
228
        endElement(xPathFragment, namespaceResolver);
229
    }
230
    
231
    /**
232
     * INTERNAL:
233
     */
234
    @Override
235
    public void marshalWithoutRootElement(ObjectBuilder treeObjectBuilder, Object object, Descriptor descriptor, Root root, boolean isXMLRoot){
236
        if(treeObjectBuilder != null){
237
            addXsiTypeAndClassIndicatorIfRequired(descriptor, null, descriptor.getDefaultRootElementField(), root, object, isXMLRoot, true);
238
            treeObjectBuilder.marshalAttributes(this, object, session);
239
        }         
240
     }
241
    
242
    /**
243
     * INTERNAL:
244
     * The character used to separate the prefix and uri portions when namespaces are present 
245
     * @since 2.4
246
     */
247
    public char getNamespaceSeparator(){        
248
        return marshaller.getNamespaceSeparator();
249
    }
250
    
251
    /**
252
     * INTERNAL:
253
     * The optional fragment used to wrap the text() mappings
254
     * @since 2.4
255
     */
256
    public XPathFragment getTextWrapperFragment() {
257
        return textWrapperFragment;
258
    }
259
    
260
    @Override
261
    public boolean isWrapperAsCollectionName() {
262
        return marshaller.isWrapperAsCollectionName();
263
    }
264
    
265
    @Override
266
    public void element(XPathFragment frag) {
267
        isLastEventStart = false;
268
    }
269
    
270
    /**
271
     * Handle marshal of an empty collection.  
272
     * @param xPathFragment
273
     * @param namespaceResolver
274
     * @param openGrouping if grouping elements should be marshalled for empty collections
275
     * @return
276
     */    
277
    public boolean emptyCollection(XPathFragment xPathFragment, NamespaceResolver namespaceResolver, boolean openGrouping) {
278
279
         if(marshaller.isMarshalEmptyCollections()){
280
             super.emptyCollection(xPathFragment, namespaceResolver, true);
281
             
282
             if (null != xPathFragment) {
283
                 
284
                 if(xPathFragment.isSelfFragment() || xPathFragment.nameIsText()){
285
                     String keyName = position.getKeyName();
286
                     position.setComplex(false);
287
                     position.parentLevel.getJsonObjectBuilder().add(keyName, Json.createArrayBuilder());                     
288
                 }else{ 
289
                     if(isLastEventStart){
290
                         position.setComplex(true);                  
291
                     }                 
292
                     String keyName =  getKeyName(xPathFragment);
293
                     if(keyName != null){
294
                        position.getJsonObjectBuilder().add(keyName, Json.createArrayBuilder());
295
                     }
296
                 }
297
                 isLastEventStart = false;   
298
             }
299
                  
300
             return true;
301
         }else{
302
             return super.emptyCollection(xPathFragment, namespaceResolver, openGrouping);
303
         }
304
    }
305
306
    @Override
307
    public void attribute(XPathFragment xPathFragment,NamespaceResolver namespaceResolver, String value) {
308
        attribute(xPathFragment, namespaceResolver, value, null);
309
    }
310
311
    @Override
312
    public void attribute(String namespaceURI, String localName, String qName, String value) {
313
        XPathFragment xPathFragment = new XPathFragment();
314
        xPathFragment.setNamespaceURI(namespaceURI);
315
        xPathFragment.setAttribute(true);
316
        xPathFragment.setLocalName(localName);
317
318
        openStartElement(xPathFragment, namespaceResolver);
319
        characters(null, value, null, false, true);
320
321
        endElement(xPathFragment, namespaceResolver);
322
        
323
    }
324
325
    @Override
326
    public void closeStartElement() {}
327
328
    @Override
329
    public void endElement(XPathFragment xPathFragment,NamespaceResolver namespaceResolver) {
330
        if(position != null){
331
            if(isLastEventStart){
332
                position.setComplex(true);
333
            }
334
            if(position.isComplex){
335
                popAndSetInParentBuilder();
336
            }else{
337
                position = position.parentLevel;
338
            }            
339
            isLastEventStart = false;          
340
        }
341
    }
342
343
    @Override
344
    public void characters(String value) {
345
        writeValue(value, null, false);
346
    }
347
348
    @Override
349
    public void characters(QName schemaType, Object value, String mimeType, boolean isCDATA){          
350
        characters(schemaType, value, mimeType, isCDATA, false);
351
     }
352
    
353
    public void characters(QName schemaType, Object value, String mimeType, boolean isCDATA, boolean isAttribute){
354
        if(mimeType != null) {
355
            if(value instanceof List){
356
               value = XMLBinaryDataHelper.getXMLBinaryDataHelper().getBytesListForBinaryValues((List)value, marshaller, mimeType);
357
           }else{
358
359
            value = XMLBinaryDataHelper.getXMLBinaryDataHelper().getBytesForBinaryValue(value, marshaller, mimeType).getData();
360
           }
361
        }         
362
        if(schemaType != null && Constants.QNAME_QNAME.equals(schemaType)){
363
            String convertedValue = getStringForQName((QName)value);
364
            writeValue(convertedValue, null, isAttribute);
365
        } 
366
        else if(value.getClass() == String.class){          
367
            //if schemaType is set and it's a numeric or boolean type don't treat as a string
368
            if(schemaType != null && isNumericOrBooleanType(schemaType)){
369
                Class theClass = (Class) ((XMLConversionManager) session.getDatasourcePlatform().getConversionManager()).getDefaultXMLTypes().get(schemaType);
370
                Object convertedValue = ((ConversionManager) session.getDatasourcePlatform().getConversionManager()).convertObject(value, theClass, schemaType);
371
                writeValue(convertedValue, schemaType, isAttribute);
372
            }else if(isCDATA){
373
                cdata((String)value);
374
            }else{
375
                writeValue((String)value, null, isAttribute);                
376
            }
377
       }else{
378
           Class theClass = (Class) ((XMLConversionManager) session.getDatasourcePlatform().getConversionManager()).getDefaultXMLTypes().get(schemaType);          
379
380
           if(schemaType == null || theClass == null){
381
               if(value.getClass() == CoreClassConstants.BOOLEAN || CoreClassConstants.NUMBER.isAssignableFrom(value.getClass())){
382
                   writeValue(value, schemaType, isAttribute);
383
               }else{
384
                   String convertedValue = ((String) ((ConversionManager) session.getDatasourcePlatform().getConversionManager()).convertObject(value, CoreClassConstants.STRING, schemaType));
385
                   writeValue(convertedValue, schemaType, isAttribute);
386
               }
387
           }else if(schemaType != null && !isNumericOrBooleanType(schemaType)){
388
               //if schemaType exists and is not boolean or number do write quotes (convert to string)
389
               String convertedValue = ((String) ((ConversionManager) session.getDatasourcePlatform().getConversionManager()).convertObject(value, CoreClassConstants.STRING, schemaType));
390
               writeValue(convertedValue, schemaType, isAttribute);
391
           } else if(isCDATA){
392
               String convertedValue = ((String) ((ConversionManager) session.getDatasourcePlatform().getConversionManager()).convertObject(value, CoreClassConstants.STRING, schemaType));
393
               cdata(convertedValue);
394
           }else{
395
               writeValue(value, schemaType, isAttribute);           
396
           }
397
       }        
398
    }
399
    
400
    
401
    private boolean isNumericOrBooleanType(QName schemaType){
402
        if(schemaType == null){
403
            return false;
404
        }else if(schemaType.equals(Constants.BOOLEAN_QNAME)
405
                || schemaType.equals(Constants.INTEGER_QNAME)
406
                || schemaType.equals(Constants.INT_QNAME)
407
                || schemaType.equals(Constants.BYTE_QNAME)
408
                || schemaType.equals(Constants.DECIMAL_QNAME)
409
                || schemaType.equals(Constants.FLOAT_QNAME)
410
                || schemaType.equals(Constants.DOUBLE_QNAME)
411
                || schemaType.equals(Constants.SHORT_QNAME)
412
                || schemaType.equals(Constants.LONG_QNAME)
413
                || schemaType.equals(Constants.NEGATIVE_INTEGER_QNAME)
414
                || schemaType.equals(Constants.NON_NEGATIVE_INTEGER_QNAME)
415
                || schemaType.equals(Constants.NON_POSITIVE_INTEGER_QNAME)
416
                || schemaType.equals(Constants.POSITIVE_INTEGER_QNAME)
417
                || schemaType.equals(Constants.UNSIGNED_BYTE_QNAME)
418
                || schemaType.equals(Constants.UNSIGNED_INT_QNAME)
419
                || schemaType.equals(Constants.UNSIGNED_LONG_QNAME)
420
                || schemaType.equals(Constants.UNSIGNED_SHORT_QNAME)
421
        ){
422
            return true;
423
        }
424
        return false;
425
    }
426
    
427
    public void writeValue(Object value, QName schemaType, boolean isAttribute) {
428
        
429
        if (characterEscapeHandler != null && value instanceof String) {
430
            try {
431
                StringWriter stringWriter = new StringWriter();
432
                characterEscapeHandler.escape(((String)value).toCharArray(), 0, ((String)value).length(), isAttribute, stringWriter);
433
                value = stringWriter.toString();
434
            } catch (IOException e) {
435
                throw XMLMarshalException.marshalException(e);
436
            }
437
        }
438
        
439
        boolean textWrapperOpened = false;                       
440
        if(!isLastEventStart){
441
             openStartElement(textWrapperFragment, namespaceResolver);
442
             textWrapperOpened = true;
443
        }
444
      
445
        Level currentLevel = position;
446
        String keyName = position.getKeyName();
447
        if(!position.isComplex){           
448
            currentLevel = position.parentLevel;
449
            currentLevel.setComplex(true);          
450
        }
451
        if(currentLevel.isCollection()){
452
            currentLevel.setEmptyCollection(false);
453
            addValueToArrayBuilder(currentLevel.getJsonArrayBuilder(), value, schemaType);            
454
        } else {
455
            JsonObjectBuilder builder = currentLevel.getJsonObjectBuilder();                   
456
            addValueToObjectBuilder(builder, keyName, value, schemaType);          
457
        }
458
        isLastEventStart = false;
459
        if(textWrapperOpened){    
460
             endElement(textWrapperFragment, namespaceResolver);
461
        }    
462
    }
463
    
464
    private void addValueToObjectBuilder(JsonObjectBuilder jsonObjectBuilder, String keyName, Object value, QName schemaType){
465
        if(value == NULL){
466
            jsonObjectBuilder.addNull(keyName);
467
        }else if(value instanceof Integer){
468
            jsonObjectBuilder.add(keyName, (Integer)value);  
469
        }else if(value instanceof BigDecimal){
470
            jsonObjectBuilder.add(keyName, (BigDecimal)value);   
471
        }else if(value instanceof BigInteger){
472
            jsonObjectBuilder.add(keyName, (BigInteger)value);               
473
        }else if(value instanceof Boolean){
474
            jsonObjectBuilder.add(keyName, (Boolean)value);
475
        }else if(value instanceof Character){
476
            jsonObjectBuilder.add(keyName, (Character)value);  
477
        }else if(value instanceof Double){
478
            jsonObjectBuilder.add(keyName, (Double)value);
479
        }else if(value instanceof Float){
480
            jsonObjectBuilder.add(keyName, (Float)value);
481
        }else if(value instanceof Long){
482
            jsonObjectBuilder.add(keyName, (Long)value);
483
        }else if(value instanceof String){
484
            jsonObjectBuilder.add(keyName, (String)value);                
485
        }else{
486
            String convertedValue = ((String) ((ConversionManager) session.getDatasourcePlatform().getConversionManager()).convertObject(value, CoreClassConstants.STRING, schemaType));
487
            Class theClass = (Class) ((XMLConversionManager) session.getDatasourcePlatform().getConversionManager()).getDefaultXMLTypes().get(schemaType);          
488
            if((schemaType == null || theClass == null) && (CoreClassConstants.NUMBER.isAssignableFrom(value.getClass()))){
489
                //if it's still a number and falls through the cracks we dont want "" around the value
490
                    BigDecimal convertedNumberValue = ((BigDecimal) ((ConversionManager) session.getDatasourcePlatform().getConversionManager()).convertObject(value, CoreClassConstants.BIGDECIMAL, schemaType));
491
                    jsonObjectBuilder.add(keyName, (BigDecimal)convertedNumberValue);
492
            }else{
493
                jsonObjectBuilder.add(keyName, convertedValue);
494
            }
495
                
496
        }
497
    }
498
    
499
    private void addValueToArrayBuilder(JsonArrayBuilder jsonArrayBuilder, Object value, QName schemaType){
500
        if(value == NULL){
501
            jsonArrayBuilder.addNull();
502
        }else if(value instanceof Integer){
503
            jsonArrayBuilder.add((Integer)value);  
504
        }else if(value instanceof BigDecimal){
505
            jsonArrayBuilder.add((BigDecimal)value);   
506
        }else if(value instanceof BigInteger){
507
            jsonArrayBuilder.add((BigInteger)value);               
508
        }else if(value instanceof Boolean){                
509
            jsonArrayBuilder.add((Boolean)value);
510
        }else if(value instanceof Character){
511
            jsonArrayBuilder.add((Character)value);  
512
        }else if(value instanceof Double){
513
            jsonArrayBuilder.add((Double)value);
514
        }else if(value instanceof Float){
515
            jsonArrayBuilder.add((Float)value);
516
        }else if(value instanceof Long){
517
            jsonArrayBuilder.add((Long)value);
518
        }else if(value instanceof String){
519
            jsonArrayBuilder.add((String)value);
520
        }else{
521
            String convertedValue = ((String) ((ConversionManager) session.getDatasourcePlatform().getConversionManager()).convertObject(value, CoreClassConstants.STRING, schemaType));
522
            Class theClass = (Class) ((XMLConversionManager) session.getDatasourcePlatform().getConversionManager()).getDefaultXMLTypes().get(schemaType);          
523
            if((schemaType == null || theClass == null) && (CoreClassConstants.NUMBER.isAssignableFrom(value.getClass()))){
524
                //if it's still a number and falls through the cracks we dont want "" around the value
525
                    BigDecimal convertedNumberValue = ((BigDecimal) ((ConversionManager) session.getDatasourcePlatform().getConversionManager()).convertObject(value, CoreClassConstants.BIGDECIMAL, schemaType));
526
                    jsonArrayBuilder.add((BigDecimal)convertedNumberValue);
527
            }else{
528
                jsonArrayBuilder.add(convertedValue);
529
            }
530
        }
531
    }
532
    
533
    @Override
534
    public void cdata(String value) {
535
        characters(value);        
536
    }
537
538
    @Override
539
    public void node(Node node, NamespaceResolver resolver, String uri, String name) {
540
       
541
        if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
542
            Attr attr = (Attr) node;
543
            String resolverPfx = null;
544
            if (getNamespaceResolver() != null) {
545
                resolverPfx = this.getNamespaceResolver().resolveNamespaceURI(attr.getNamespaceURI());
546
            }
547
            String namespaceURI = attr.getNamespaceURI();
548
            // If the namespace resolver contains a prefix for the attribute's URI,
549
            // use it instead of what is set on the attribute
550
            if (resolverPfx != null) {
551
                attribute(attr.getNamespaceURI(), Constants.EMPTY_STRING, resolverPfx+Constants.COLON+attr.getLocalName(), attr.getNodeValue());
552
            } else {
553
                attribute(attr.getNamespaceURI(), Constants.EMPTY_STRING, attr.getName(), attr.getNodeValue());
554
                // May need to declare the URI locally
555
                if (attr.getNamespaceURI() != null) {
556
                    attribute(javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI, Constants.EMPTY_STRING, javax.xml.XMLConstants.XMLNS_ATTRIBUTE + Constants.COLON + attr.getPrefix(), attr.getNamespaceURI());
557
                    this.getNamespaceResolver().put(attr.getPrefix(), attr.getNamespaceURI());
558
                }
559
            }
560
        } else if (node.getNodeType() == Node.TEXT_NODE) {
561
            writeValue(node.getNodeValue(), null, false);
562
        } else {
563
            try {
564
                JsonBuilderRecordContentHandler wrcHandler = new JsonBuilderRecordContentHandler();
565
                
566
                XMLFragmentReader xfragReader = new XMLFragmentReader(namespaceResolver);
567
                xfragReader.setContentHandler(wrcHandler);
568
                xfragReader.setProperty("http://xml.org/sax/properties/lexical-handler", wrcHandler);
569
                xfragReader.parse(node, uri, name);
570
            } catch (SAXException sex) {
571
                throw XMLMarshalException.marshalException(sex);
572
            }
573
        }
574
        
575
    }        
576
    
577
    protected String getStringForQName(QName qName){
578
        if(null == qName) {
579
            return null;
580
        }
581
        CoreConversionManager xmlConversionManager = getSession().getDatasourcePlatform().getConversionManager();
582
583
        return (String) xmlConversionManager.convertObject(qName, String.class);       
584
    }
585
586
    /**
587
     * INTERNAL:
588
     */
589
     public void namespaceDeclarations(NamespaceResolver namespaceResolver) {
590
     }
591
592
     public void namespaceDeclaration(String prefix, String namespaceURI){
593
     }
594
     
595
     public void defaultNamespaceDeclaration(String defaultNamespace){
596
     }
597
     
598
    /**
599
     * INTERNAL:
600
     */
601
     public void nilComplex(XPathFragment xPathFragment, NamespaceResolver namespaceResolver){
602
         XPathFragment groupingFragment = openStartGroupingElements(namespaceResolver);
603
         closeStartGroupingElements(groupingFragment);
604
         openStartElement(xPathFragment, namespaceResolver);
605
         characters(NULL);
606
         endElement(xPathFragment, namespaceResolver);
607
     }
608
609
    /**
610
     * INTERNAL:
611
     */
612
     public void nilSimple(NamespaceResolver namespaceResolver){
613
         XPathFragment groupingFragment = openStartGroupingElements(namespaceResolver);         
614
         characters(NULL);        
615
         closeStartGroupingElements(groupingFragment);
616
     }
617
618
     /**
619
      * Used when an empty simple value should be written
620
      * @since EclipseLink 2.4
621
      */
622
     public void emptySimple(NamespaceResolver namespaceResolver){
623
         nilSimple(namespaceResolver);
624
     }
625
     
626
     public void emptyAttribute(XPathFragment xPathFragment,NamespaceResolver namespaceResolver){
627
         XPathFragment groupingFragment = openStartGroupingElements(namespaceResolver);
628
         openStartElement(xPathFragment, namespaceResolver);
629
         characters(NULL);
630
         endElement(xPathFragment, namespaceResolver);
631
         closeStartGroupingElements(groupingFragment);
632
     }
633
634
     /**
635
      * Used when an empty complex item should be written
636
      * @since EclipseLink 2.4
637
      */
638
     public void emptyComplex(XPathFragment xPathFragment, NamespaceResolver namespaceResolver){
639
         XPathFragment groupingFragment = openStartGroupingElements(namespaceResolver);
640
         closeStartGroupingElements(groupingFragment);
641
         openStartElement(xPathFragment, namespaceResolver);
642
         endElement(xPathFragment, namespaceResolver);
643
     }
644
    
645
    
646
     
647
     /**
648
      * This class will typically be used in conjunction with an XMLFragmentReader.
649
      * The XMLFragmentReader will walk a given XMLFragment node and report events
650
      * to this class - the event's data is then written to the enclosing class'
651
      * writer.
652
      *
653
      * @see org.eclipse.persistence.internal.oxm.record.XMLFragmentReader
654
      */
655
     protected class JsonBuilderRecordContentHandler implements ExtendedContentHandler, LexicalHandler {
656
657
         JsonBuilderRecordContentHandler() {
658
         }
659
660
         // --------------------- CONTENTHANDLER METHODS --------------------- //
661
         public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
662
                 XPathFragment xPathFragment = new XPathFragment(localName);
663
                 xPathFragment.setNamespaceURI(namespaceURI);
664
                 openStartElement(xPathFragment, namespaceResolver);
665
                 handleAttributes(atts);
666
         }
667
668
         public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
669
             XPathFragment xPathFragment = new XPathFragment(localName);
670
             xPathFragment.setNamespaceURI(namespaceURI);
671
             
672
             JsonBuilderWriterRecord.this.endElement(xPathFragment, namespaceResolver);        
673
         }
674
675
         public void startPrefixMapping(String prefix, String uri) throws SAXException {
676
         }
677
678
         public void characters(char[] ch, int start, int length) throws SAXException {
679
             String characters = new String (ch, start, length);
680
             characters(characters);
681
         }
682
683
         public void characters(CharSequence characters) throws SAXException {           
684
             JsonBuilderWriterRecord.this.characters(characters.toString());      
685
         }
686
687
         // --------------------- LEXICALHANDLER METHODS --------------------- //
688
         public void comment(char[] ch, int start, int length) throws SAXException {
689
         }
690
691
         public void startCDATA() throws SAXException {
692
         }
693
694
         public void endCDATA() throws SAXException {
695
         }
696
697
         // --------------------- CONVENIENCE METHODS --------------------- //
698
            protected void handleAttributes(Attributes atts) {
699
             for (int i=0, attsLength = atts.getLength(); i<attsLength; i++) {
700
                 String qName = atts.getQName(i);
701
                 if((qName != null && (qName.startsWith(javax.xml.XMLConstants.XMLNS_ATTRIBUTE + Constants.COLON) || qName.equals(javax.xml.XMLConstants.XMLNS_ATTRIBUTE)))) {
702
                     continue;
703
                 }
704
                 attribute(atts.getURI(i), atts.getLocalName(i), qName, atts.getValue(i));
705
             }
706
         }
707
708
         protected void writeComment(char[] chars, int start, int length) {        
709
         }
710
        
711
         protected void writeCharacters(char[] chars, int start, int length) {
712
             try {
713
                 characters(chars, start, length);
714
             } catch (SAXException e) {
715
                 throw XMLMarshalException.marshalException(e);
716
             }           
717
         }
718
         // --------------- SATISFY CONTENTHANDLER INTERFACE --------------- //
719
         public void endPrefixMapping(String prefix) throws SAXException {}
720
         public void processingInstruction(String target, String data) throws SAXException {}
721
         public void setDocumentLocator(Locator locator) {}
722
         public void startDocument() throws SAXException {}
723
         public void endDocument() throws SAXException {}
724
         public void skippedEntity(String name) throws SAXException {}
725
         public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {}
726
727
         // --------------- SATISFY LEXICALHANDLER INTERFACE --------------- //
728
         public void startEntity(String name) throws SAXException {}
729
         public void endEntity(String name) throws SAXException {}
730
         public void startDTD(String name, String publicId, String systemId) throws SAXException {}
731
         public void endDTD() throws SAXException {}
732
         @Override
733
         public void setNil(boolean isNil) {}
734
735
     }
736
737
     
738
     /**
739
     * Instances of this class are used to maintain state about the current
740
     * level of the JSON message being marshalled.
741
     */
742
    protected static class Level {
743
        
744
        private boolean isCollection;
745
        private boolean emptyCollection;
746
        private String keyName;        
747
        private JsonObjectBuilder jsonObjectBuilder;
748
        private JsonArrayBuilder jsonArrayBuilder;
749
        private boolean isComplex;
750
        private Level parentLevel;
751
        
752
        public Level(boolean isCollection, Level parentLevel) {
753
            setCollection(isCollection);
754
            emptyCollection = true;
755
            this.parentLevel = parentLevel;
756
        }
757
758
        public boolean isCollection() {
759
            return isCollection;
760
        }
761
762
        public void setCollection(boolean isCollection) {
763
            this.isCollection = isCollection;
764
            if(isCollection && jsonArrayBuilder == null){
765
                jsonArrayBuilder = Json.createArrayBuilder();
766
            }
767
        }
768
769
        public String getKeyName() {
770
            return keyName;
771
        }
772
773
        public void setKeyName(String keyName) {
774
            this.keyName = keyName;
775
        }
776
777
        public JsonObjectBuilder getJsonObjectBuilder() {
778
            return jsonObjectBuilder;
779
        }
780
781
        public void setJsonObjectBuilder(JsonObjectBuilder jsonObjectBuilder) {
782
            this.jsonObjectBuilder = jsonObjectBuilder;
783
        }
784
785
        public JsonArrayBuilder getJsonArrayBuilder() {
786
            return jsonArrayBuilder;
787
        }
788
789
        public void setJsonArrayBuilder(JsonArrayBuilder jsonArrayBuilder) {
790
            this.jsonArrayBuilder = jsonArrayBuilder;
791
        }
792
793
        public boolean isEmptyCollection() {
794
            return emptyCollection;
795
        }
796
797
        public void setEmptyCollection(boolean emptyCollection) {
798
            this.emptyCollection = emptyCollection;
799
        }
800
        public boolean isComplex() {
801
            return isComplex;
802
        }
803
804
        public void setComplex(boolean isComplex) {
805
            this.isComplex = isComplex;
806
            if(isComplex && jsonObjectBuilder == null){
807
                jsonObjectBuilder = Json.createObjectBuilder();
808
            }
809
        }
810
811
    }
812
813
}
(-)a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/oxm/record/JsonObjectBuilderWriterRecord.java (-813 lines)
Lines 1-813 Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
3
 * This program and the accompanying materials are made available under the
4
 * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
5
 * which accompanies this distribution.
6
 * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
7
 * and the Eclipse Distribution License is available at
8
 * http://www.eclipse.org/org/documents/edl-v10.php.
9
 *
10
 * Contributors:
11
 *     Denise Smith - 2.6 - initial implementation
12
 ******************************************************************************/
13
package org.eclipse.persistence.oxm.record;
14
15
import java.io.IOException;
16
import java.io.StringWriter;
17
import java.math.BigDecimal;
18
import java.math.BigInteger;
19
import java.util.List;
20
21
import javax.json.Json;
22
import javax.json.JsonArrayBuilder;
23
import javax.json.JsonObjectBuilder;
24
import javax.xml.namespace.QName;
25
26
import org.eclipse.persistence.exceptions.XMLMarshalException;
27
import org.eclipse.persistence.internal.core.helper.CoreClassConstants;
28
import org.eclipse.persistence.internal.core.helper.CoreConversionManager;
29
import org.eclipse.persistence.internal.oxm.CharacterEscapeHandler;
30
import org.eclipse.persistence.internal.oxm.Constants;
31
import org.eclipse.persistence.internal.oxm.ConversionManager;
32
import org.eclipse.persistence.internal.oxm.NamespaceResolver;
33
import org.eclipse.persistence.internal.oxm.ObjectBuilder;
34
import org.eclipse.persistence.internal.oxm.Root;
35
import org.eclipse.persistence.internal.oxm.XMLBinaryDataHelper;
36
import org.eclipse.persistence.internal.oxm.XMLMarshaller;
37
import org.eclipse.persistence.internal.oxm.XPathFragment;
38
import org.eclipse.persistence.internal.oxm.XMLConversionManager;
39
import org.eclipse.persistence.internal.oxm.mappings.Descriptor;
40
import org.eclipse.persistence.internal.oxm.record.ExtendedContentHandler;
41
import org.eclipse.persistence.internal.oxm.record.XMLFragmentReader;
42
import org.w3c.dom.Attr;
43
import org.w3c.dom.Node;
44
import org.xml.sax.Attributes;
45
import org.xml.sax.Locator;
46
import org.xml.sax.SAXException;
47
import org.xml.sax.ext.LexicalHandler;
48
49
public class JsonObjectBuilderWriterRecord extends MarshalRecord <XMLMarshaller> {
50
51
    private Level position;
52
    private JsonObjectBuilder rootJsonObjectBuilder;
53
    private JsonArrayBuilder rootJsonArrayBuilder;
54
    private CharacterEscapeHandler characterEscapeHandler;
55
56
    private String attributePrefix;
57
    private boolean isRootArray;
58
    private static final String NULL="null";
59
    private boolean isLastEventStart;
60
        
61
    public JsonObjectBuilderWriterRecord(){
62
        super();
63
        isLastEventStart = false;
64
    }
65
    
66
    public JsonObjectBuilderWriterRecord(JsonObjectBuilder jsonObjectBuilder){
67
        this();
68
        rootJsonObjectBuilder = jsonObjectBuilder;
69
    }
70
    
71
    public JsonObjectBuilderWriterRecord(JsonArrayBuilder jsonArrayBuilder){
72
        this();
73
        rootJsonArrayBuilder = jsonArrayBuilder;
74
        isRootArray = true;
75
    }
76
    
77
    /**
78
     * INTERNAL:
79
     */
80
    public void setMarshaller(XMLMarshaller marshaller) {
81
        super.setMarshaller(marshaller);
82
        attributePrefix = marshaller.getAttributePrefix();
83
        if (marshaller.getValueWrapper() != null) {
84
            textWrapperFragment = new XPathFragment();
85
            textWrapperFragment.setLocalName(marshaller.getValueWrapper());
86
        }
87
        characterEscapeHandler = marshaller.getCharacterEscapeHandler();
88
    }
89
    
90
    @Override
91
    public void startDocument(String encoding, String version) {      
92
        if(isRootArray){
93
            if(position == null){
94
                startCollection();
95
            }
96
            position.setEmptyCollection(false);
97
            
98
            Level newLevel = new Level(false, position);
99
            position = newLevel;
100
            
101
            isLastEventStart = true;
102
        }else{
103
            Level rootLevel = new Level(false, null);
104
            position = rootLevel;
105
            if(rootJsonObjectBuilder == null){
106
                rootJsonObjectBuilder = Json.createObjectBuilder();
107
            }  
108
            
109
            rootLevel.setJsonObjectBuilder(rootJsonObjectBuilder);
110
        }
111
    }
112
113
    @Override
114
    public void endDocument() {
115
        if(position != null){
116
            if(position.parentLevel != null && position.parentLevel.isCollection){
117
                popAndSetInParentBuilder();
118
            }else{
119
                //this is the root level list case
120
                position = position.parentLevel;
121
            }
122
        }
123
    }
124
    
125
    private void popAndSetInParentBuilder(){
126
        Level removedLevel = position;
127
        Level parentLevel = position.parentLevel;
128
        position = position.parentLevel;
129
        if(removedLevel.isCollection && removedLevel.isEmptyCollection() && removedLevel.keyName == null){
130
            return;
131
        }
132
      
133
        if(parentLevel != null){                  
134
            if(parentLevel.isCollection){
135
                if(removedLevel.isCollection){
136
                    parentLevel.getJsonArrayBuilder().add(removedLevel.getJsonArrayBuilder());
137
                }else{                   
138
                    parentLevel.getJsonArrayBuilder().add(removedLevel.getJsonObjectBuilder());
139
                }
140
            }else{
141
                if(removedLevel.isCollection){                    
142
                    parentLevel.getJsonObjectBuilder().add(removedLevel.getKeyName(), removedLevel.getJsonArrayBuilder());
143
                }else{
144
                    parentLevel.getJsonObjectBuilder().add(removedLevel.getKeyName(), removedLevel.getJsonObjectBuilder());
145
                }
146
            }
147
        }
148
        
149
    }    
150
    
151
    public void startCollection() {
152
        if(position == null){
153
             isRootArray = true;              
154
             Level rootLevel = new Level(true, null);
155
             if(rootJsonArrayBuilder == null){
156
                  rootJsonArrayBuilder = Json.createArrayBuilder();
157
             }
158
             rootLevel.setJsonArrayBuilder(rootJsonArrayBuilder);
159
             position = rootLevel;
160
        } else {            
161
            if(isLastEventStart){
162
                position.setComplex(true);           
163
            }            
164
            Level level = new Level(true, position);            
165
            position = level;
166
        }      
167
        isLastEventStart = false;
168
    }
169
    
170
    @Override
171
    public void endCollection() {
172
         popAndSetInParentBuilder();    
173
    }
174
    
175
    @Override    
176
    public void openStartElement(XPathFragment xPathFragment, NamespaceResolver namespaceResolver) {
177
        super.openStartElement(xPathFragment, namespaceResolver);
178
        if(position != null){
179
            Level newLevel = new Level(false, position);            
180
            
181
            if(isLastEventStart){ 
182
                //this means 2 startevents in a row so the last this is a complex object
183
                position.setComplex(true);                                
184
            }
185
                      
186
            String keyName = getKeyName(xPathFragment);
187
           
188
            if(position.isCollection && position.isEmptyCollection() ){
189
                position.setKeyName(keyName);
190
            }else{
191
                newLevel.setKeyName(keyName);    
192
            }
193
            position = newLevel;   
194
            isLastEventStart = true;
195
        }
196
    }
197
    
198
    protected String getKeyName(XPathFragment xPathFragment){
199
        String keyName = xPathFragment.getLocalName(); 
200
       
201
        if(isNamespaceAware()){
202
            if(xPathFragment.getNamespaceURI() != null){
203
                String prefix = null;
204
                if(getNamespaceResolver() !=null){
205
                    prefix = getNamespaceResolver().resolveNamespaceURI(xPathFragment.getNamespaceURI());
206
                } else if(namespaceResolver != null){
207
                    prefix = namespaceResolver.resolveNamespaceURI(xPathFragment.getNamespaceURI());
208
                }
209
                if(prefix != null && !prefix.equals(Constants.EMPTY_STRING)){
210
                    keyName = prefix + getNamespaceSeparator() +  keyName;                           
211
                }
212
            }
213
        } 
214
        if(xPathFragment.isAttribute() && attributePrefix != null){
215
            keyName = attributePrefix + keyName;
216
        }
217
218
        return keyName;
219
    }
220
221
    public void attribute(XPathFragment xPathFragment, NamespaceResolver namespaceResolver,  Object value, QName schemaType){
222
        if(xPathFragment.getNamespaceURI() != null && xPathFragment.getNamespaceURI() == javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI){
223
            return;
224
        }
225
        xPathFragment.setAttribute(true);
226
        openStartElement(xPathFragment, namespaceResolver);
227
        characters(schemaType, value, null, false, true);
228
        endElement(xPathFragment, namespaceResolver);
229
    }
230
    
231
    /**
232
     * INTERNAL:
233
     */
234
    @Override
235
    public void marshalWithoutRootElement(ObjectBuilder treeObjectBuilder, Object object, Descriptor descriptor, Root root, boolean isXMLRoot){
236
        if(treeObjectBuilder != null){
237
            addXsiTypeAndClassIndicatorIfRequired(descriptor, null, descriptor.getDefaultRootElementField(), root, object, isXMLRoot, true);
238
            treeObjectBuilder.marshalAttributes(this, object, session);
239
        }         
240
     }
241
    
242
    /**
243
     * INTERNAL:
244
     * The character used to separate the prefix and uri portions when namespaces are present 
245
     * @since 2.4
246
     */
247
    public char getNamespaceSeparator(){        
248
        return marshaller.getNamespaceSeparator();
249
    }
250
    
251
    /**
252
     * INTERNAL:
253
     * The optional fragment used to wrap the text() mappings
254
     * @since 2.4
255
     */
256
    public XPathFragment getTextWrapperFragment() {
257
        return textWrapperFragment;
258
    }
259
    
260
    @Override
261
    public boolean isWrapperAsCollectionName() {
262
        return marshaller.isWrapperAsCollectionName();
263
    }
264
    
265
    @Override
266
    public void element(XPathFragment frag) {
267
        isLastEventStart = false;
268
    }
269
    
270
    /**
271
     * Handle marshal of an empty collection.  
272
     * @param xPathFragment
273
     * @param namespaceResolver
274
     * @param openGrouping if grouping elements should be marshalled for empty collections
275
     * @return
276
     */    
277
    public boolean emptyCollection(XPathFragment xPathFragment, NamespaceResolver namespaceResolver, boolean openGrouping) {
278
279
         if(marshaller.isMarshalEmptyCollections()){
280
             super.emptyCollection(xPathFragment, namespaceResolver, true);
281
             
282
             if (null != xPathFragment) {
283
                 
284
                 if(xPathFragment.isSelfFragment() || xPathFragment.nameIsText()){
285
                     String keyName = position.getKeyName();
286
                     position.setComplex(false);
287
                     position.parentLevel.getJsonObjectBuilder().add(keyName, Json.createArrayBuilder());                     
288
                 }else{ 
289
                     if(isLastEventStart){
290
                         position.setComplex(true);                  
291
                     }                 
292
                     String keyName =  getKeyName(xPathFragment);
293
                     if(keyName != null){
294
                        position.getJsonObjectBuilder().add(keyName, Json.createArrayBuilder());
295
                     }
296
                 }
297
                 isLastEventStart = false;   
298
             }
299
                  
300
             return true;
301
         }else{
302
             return super.emptyCollection(xPathFragment, namespaceResolver, openGrouping);
303
         }
304
    }
305
306
    @Override
307
    public void attribute(XPathFragment xPathFragment,NamespaceResolver namespaceResolver, String value) {
308
        attribute(xPathFragment, namespaceResolver, value, null);
309
    }
310
311
    @Override
312
    public void attribute(String namespaceURI, String localName, String qName, String value) {
313
        XPathFragment xPathFragment = new XPathFragment();
314
        xPathFragment.setNamespaceURI(namespaceURI);
315
        xPathFragment.setAttribute(true);
316
        xPathFragment.setLocalName(localName);
317
318
        openStartElement(xPathFragment, namespaceResolver);
319
        characters(null, value, null, false, true);
320
321
        endElement(xPathFragment, namespaceResolver);
322
        
323
    }
324
325
    @Override
326
    public void closeStartElement() {}
327
328
    @Override
329
    public void endElement(XPathFragment xPathFragment,NamespaceResolver namespaceResolver) {
330
        if(position != null){
331
            if(isLastEventStart){
332
                position.setComplex(true);
333
            }
334
            if(position.isComplex){
335
                popAndSetInParentBuilder();
336
            }else{
337
                position = position.parentLevel;
338
            }            
339
            isLastEventStart = false;          
340
        }
341
    }
342
343
    @Override
344
    public void characters(String value) {
345
        writeValue(value, null, false);
346
    }
347
348
    @Override
349
    public void characters(QName schemaType, Object value, String mimeType, boolean isCDATA){          
350
        characters(schemaType, value, mimeType, isCDATA, false);
351
     }
352
    
353
    public void characters(QName schemaType, Object value, String mimeType, boolean isCDATA, boolean isAttribute){
354
        if(mimeType != null) {
355
            if(value instanceof List){
356
               value = XMLBinaryDataHelper.getXMLBinaryDataHelper().getBytesListForBinaryValues((List)value, marshaller, mimeType);
357
           }else{
358
359
            value = XMLBinaryDataHelper.getXMLBinaryDataHelper().getBytesForBinaryValue(value, marshaller, mimeType).getData();
360
           }
361
        }         
362
        if(schemaType != null && Constants.QNAME_QNAME.equals(schemaType)){
363
            String convertedValue = getStringForQName((QName)value);
364
            writeValue(convertedValue, null, isAttribute);
365
        } 
366
        else if(value.getClass() == String.class){          
367
            //if schemaType is set and it's a numeric or boolean type don't treat as a string
368
            if(schemaType != null && isNumericOrBooleanType(schemaType)){
369
                Class theClass = (Class) ((XMLConversionManager) session.getDatasourcePlatform().getConversionManager()).getDefaultXMLTypes().get(schemaType);
370
                Object convertedValue = ((ConversionManager) session.getDatasourcePlatform().getConversionManager()).convertObject(value, theClass, schemaType);
371
                writeValue(convertedValue, schemaType, isAttribute);
372
            }else if(isCDATA){
373
                cdata((String)value);
374
            }else{
375
                writeValue((String)value, null, isAttribute);                
376
            }
377
       }else{
378
           Class theClass = (Class) ((XMLConversionManager) session.getDatasourcePlatform().getConversionManager()).getDefaultXMLTypes().get(schemaType);          
379
380
           if(schemaType == null || theClass == null){
381
               if(value.getClass() == CoreClassConstants.BOOLEAN || CoreClassConstants.NUMBER.isAssignableFrom(value.getClass())){
382
                   writeValue(value, schemaType, isAttribute);
383
               }else{
384
                   String convertedValue = ((String) ((ConversionManager) session.getDatasourcePlatform().getConversionManager()).convertObject(value, CoreClassConstants.STRING, schemaType));
385
                   writeValue(convertedValue, schemaType, isAttribute);
386
               }
387
           }else if(schemaType != null && !isNumericOrBooleanType(schemaType)){
388
               //if schemaType exists and is not boolean or number do write quotes (convert to string)
389
               String convertedValue = ((String) ((ConversionManager) session.getDatasourcePlatform().getConversionManager()).convertObject(value, CoreClassConstants.STRING, schemaType));
390
               writeValue(convertedValue, schemaType, isAttribute);
391
           } else if(isCDATA){
392
               String convertedValue = ((String) ((ConversionManager) session.getDatasourcePlatform().getConversionManager()).convertObject(value, CoreClassConstants.STRING, schemaType));
393
               cdata(convertedValue);
394
           }else{
395
               writeValue(value, schemaType, isAttribute);           
396
           }
397
       }        
398
    }
399
    
400
    
401
    private boolean isNumericOrBooleanType(QName schemaType){
402
        if(schemaType == null){
403
            return false;
404
        }else if(schemaType.equals(Constants.BOOLEAN_QNAME)
405
                || schemaType.equals(Constants.INTEGER_QNAME)
406
                || schemaType.equals(Constants.INT_QNAME)
407
                || schemaType.equals(Constants.BYTE_QNAME)
408
                || schemaType.equals(Constants.DECIMAL_QNAME)
409
                || schemaType.equals(Constants.FLOAT_QNAME)
410
                || schemaType.equals(Constants.DOUBLE_QNAME)
411
                || schemaType.equals(Constants.SHORT_QNAME)
412
                || schemaType.equals(Constants.LONG_QNAME)
413
                || schemaType.equals(Constants.NEGATIVE_INTEGER_QNAME)
414
                || schemaType.equals(Constants.NON_NEGATIVE_INTEGER_QNAME)
415
                || schemaType.equals(Constants.NON_POSITIVE_INTEGER_QNAME)
416
                || schemaType.equals(Constants.POSITIVE_INTEGER_QNAME)
417
                || schemaType.equals(Constants.UNSIGNED_BYTE_QNAME)
418
                || schemaType.equals(Constants.UNSIGNED_INT_QNAME)
419
                || schemaType.equals(Constants.UNSIGNED_LONG_QNAME)
420
                || schemaType.equals(Constants.UNSIGNED_SHORT_QNAME)
421
        ){
422
            return true;
423
        }
424
        return false;
425
    }
426
    
427
    public void writeValue(Object value, QName schemaType, boolean isAttribute) {
428
        
429
        if (characterEscapeHandler != null && value instanceof String) {
430
            try {
431
                StringWriter stringWriter = new StringWriter();
432
                characterEscapeHandler.escape(((String)value).toCharArray(), 0, ((String)value).length(), isAttribute, stringWriter);
433
                value = stringWriter.toString();
434
            } catch (IOException e) {
435
                throw XMLMarshalException.marshalException(e);
436
            }
437
        }
438
        
439
        boolean textWrapperOpened = false;                       
440
        if(!isLastEventStart){
441
             openStartElement(textWrapperFragment, namespaceResolver);
442
             textWrapperOpened = true;
443
        }
444
      
445
        Level currentLevel = position;
446
        String keyName = position.getKeyName();
447
        if(!position.isComplex){           
448
            currentLevel = position.parentLevel;
449
            currentLevel.setComplex(true);          
450
        }
451
        if(currentLevel.isCollection()){
452
            currentLevel.setEmptyCollection(false);
453
            addValueToArrayBuilder(currentLevel.getJsonArrayBuilder(), value, schemaType);            
454
        } else {
455
            JsonObjectBuilder builder = currentLevel.getJsonObjectBuilder();                   
456
            addValueToObjectBuilder(builder, keyName, value, schemaType);          
457
        }
458
        isLastEventStart = false;
459
        if(textWrapperOpened){    
460
             endElement(textWrapperFragment, namespaceResolver);
461
        }    
462
    }
463
    
464
    private void addValueToObjectBuilder(JsonObjectBuilder jsonObjectBuilder, String keyName, Object value, QName schemaType){
465
        if(value == NULL){
466
            jsonObjectBuilder.addNull(keyName);
467
        }else if(value instanceof Integer){
468
            jsonObjectBuilder.add(keyName, (Integer)value);  
469
        }else if(value instanceof BigDecimal){
470
            jsonObjectBuilder.add(keyName, (BigDecimal)value);   
471
        }else if(value instanceof BigInteger){
472
            jsonObjectBuilder.add(keyName, (BigInteger)value);               
473
        }else if(value instanceof Boolean){
474
            jsonObjectBuilder.add(keyName, (Boolean)value);
475
        }else if(value instanceof Character){
476
            jsonObjectBuilder.add(keyName, (Character)value);  
477
        }else if(value instanceof Double){
478
            jsonObjectBuilder.add(keyName, (Double)value);
479
        }else if(value instanceof Float){
480
            jsonObjectBuilder.add(keyName, (Float)value);
481
        }else if(value instanceof Long){
482
            jsonObjectBuilder.add(keyName, (Long)value);
483
        }else if(value instanceof String){
484
            jsonObjectBuilder.add(keyName, (String)value);                
485
        }else{
486
            String convertedValue = ((String) ((ConversionManager) session.getDatasourcePlatform().getConversionManager()).convertObject(value, CoreClassConstants.STRING, schemaType));
487
            Class theClass = (Class) ((XMLConversionManager) session.getDatasourcePlatform().getConversionManager()).getDefaultXMLTypes().get(schemaType);          
488
            if((schemaType == null || theClass == null) && (CoreClassConstants.NUMBER.isAssignableFrom(value.getClass()))){
489
                //if it's still a number and falls through the cracks we dont want "" around the value
490
                    BigDecimal convertedNumberValue = ((BigDecimal) ((ConversionManager) session.getDatasourcePlatform().getConversionManager()).convertObject(value, CoreClassConstants.BIGDECIMAL, schemaType));
491
                    jsonObjectBuilder.add(keyName, (BigDecimal)convertedNumberValue);
492
            }else{
493
                jsonObjectBuilder.add(keyName, convertedValue);
494
            }
495
                
496
        }
497
    }
498
    
499
    private void addValueToArrayBuilder(JsonArrayBuilder jsonArrayBuilder, Object value, QName schemaType){
500
        if(value == NULL){
501
            jsonArrayBuilder.addNull();
502
        }else if(value instanceof Integer){
503
            jsonArrayBuilder.add((Integer)value);  
504
        }else if(value instanceof BigDecimal){
505
            jsonArrayBuilder.add((BigDecimal)value);   
506
        }else if(value instanceof BigInteger){
507
            jsonArrayBuilder.add((BigInteger)value);               
508
        }else if(value instanceof Boolean){                
509
            jsonArrayBuilder.add((Boolean)value);
510
        }else if(value instanceof Character){
511
            jsonArrayBuilder.add((Character)value);  
512
        }else if(value instanceof Double){
513
            jsonArrayBuilder.add((Double)value);
514
        }else if(value instanceof Float){
515
            jsonArrayBuilder.add((Float)value);
516
        }else if(value instanceof Long){
517
            jsonArrayBuilder.add((Long)value);
518
        }else if(value instanceof String){
519
            jsonArrayBuilder.add((String)value);
520
        }else{
521
            String convertedValue = ((String) ((ConversionManager) session.getDatasourcePlatform().getConversionManager()).convertObject(value, CoreClassConstants.STRING, schemaType));
522
            Class theClass = (Class) ((XMLConversionManager) session.getDatasourcePlatform().getConversionManager()).getDefaultXMLTypes().get(schemaType);          
523
            if((schemaType == null || theClass == null) && (CoreClassConstants.NUMBER.isAssignableFrom(value.getClass()))){
524
                //if it's still a number and falls through the cracks we dont want "" around the value
525
                    BigDecimal convertedNumberValue = ((BigDecimal) ((ConversionManager) session.getDatasourcePlatform().getConversionManager()).convertObject(value, CoreClassConstants.BIGDECIMAL, schemaType));
526
                    jsonArrayBuilder.add((BigDecimal)convertedNumberValue);
527
            }else{
528
                jsonArrayBuilder.add(convertedValue);
529
            }
530
        }
531
    }
532
    
533
    @Override
534
    public void cdata(String value) {
535
        characters(value);        
536
    }
537
538
    @Override
539
    public void node(Node node, NamespaceResolver resolver, String uri, String name) {
540
       
541
        if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
542
            Attr attr = (Attr) node;
543
            String resolverPfx = null;
544
            if (getNamespaceResolver() != null) {
545
                resolverPfx = this.getNamespaceResolver().resolveNamespaceURI(attr.getNamespaceURI());
546
            }
547
            String namespaceURI = attr.getNamespaceURI();
548
            // If the namespace resolver contains a prefix for the attribute's URI,
549
            // use it instead of what is set on the attribute
550
            if (resolverPfx != null) {
551
                attribute(attr.getNamespaceURI(), Constants.EMPTY_STRING, resolverPfx+Constants.COLON+attr.getLocalName(), attr.getNodeValue());
552
            } else {
553
                attribute(attr.getNamespaceURI(), Constants.EMPTY_STRING, attr.getName(), attr.getNodeValue());
554
                // May need to declare the URI locally
555
                if (attr.getNamespaceURI() != null) {
556
                    attribute(javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI, Constants.EMPTY_STRING, javax.xml.XMLConstants.XMLNS_ATTRIBUTE + Constants.COLON + attr.getPrefix(), attr.getNamespaceURI());
557
                    this.getNamespaceResolver().put(attr.getPrefix(), attr.getNamespaceURI());
558
                }
559
            }
560
        } else if (node.getNodeType() == Node.TEXT_NODE) {
561
            writeValue(node.getNodeValue(), null, false);
562
        } else {
563
            try {
564
                JsonObjectBuilderRecordContentHandler wrcHandler = new JsonObjectBuilderRecordContentHandler();
565
                
566
                XMLFragmentReader xfragReader = new XMLFragmentReader(namespaceResolver);
567
                xfragReader.setContentHandler(wrcHandler);
568
                xfragReader.setProperty("http://xml.org/sax/properties/lexical-handler", wrcHandler);
569
                xfragReader.parse(node, uri, name);
570
            } catch (SAXException sex) {
571
                throw XMLMarshalException.marshalException(sex);
572
            }
573
        }
574
        
575
    }        
576
    
577
    protected String getStringForQName(QName qName){
578
        if(null == qName) {
579
            return null;
580
        }
581
        CoreConversionManager xmlConversionManager = getSession().getDatasourcePlatform().getConversionManager();
582
583
        return (String) xmlConversionManager.convertObject(qName, String.class);       
584
    }
585
586
    /**
587
     * INTERNAL:
588
     */
589
     public void namespaceDeclarations(NamespaceResolver namespaceResolver) {
590
     }
591
592
     public void namespaceDeclaration(String prefix, String namespaceURI){
593
     }
594
     
595
     public void defaultNamespaceDeclaration(String defaultNamespace){
596
     }
597
     
598
    /**
599
     * INTERNAL:
600
     */
601
     public void nilComplex(XPathFragment xPathFragment, NamespaceResolver namespaceResolver){
602
         XPathFragment groupingFragment = openStartGroupingElements(namespaceResolver);
603
         closeStartGroupingElements(groupingFragment);
604
         openStartElement(xPathFragment, namespaceResolver);
605
         characters(NULL);
606
         endElement(xPathFragment, namespaceResolver);
607
     }
608
609
    /**
610
     * INTERNAL:
611
     */
612
     public void nilSimple(NamespaceResolver namespaceResolver){
613
         XPathFragment groupingFragment = openStartGroupingElements(namespaceResolver);         
614
         characters(NULL);        
615
         closeStartGroupingElements(groupingFragment);
616
     }
617
618
     /**
619
      * Used when an empty simple value should be written
620
      * @since EclipseLink 2.4
621
      */
622
     public void emptySimple(NamespaceResolver namespaceResolver){
623
         nilSimple(namespaceResolver);
624
     }
625
     
626
     public void emptyAttribute(XPathFragment xPathFragment,NamespaceResolver namespaceResolver){
627
         XPathFragment groupingFragment = openStartGroupingElements(namespaceResolver);
628
         openStartElement(xPathFragment, namespaceResolver);
629
         characters(NULL);
630
         endElement(xPathFragment, namespaceResolver);
631
         closeStartGroupingElements(groupingFragment);
632
     }
633
634
     /**
635
      * Used when an empty complex item should be written
636
      * @since EclipseLink 2.4
637
      */
638
     public void emptyComplex(XPathFragment xPathFragment, NamespaceResolver namespaceResolver){
639
         XPathFragment groupingFragment = openStartGroupingElements(namespaceResolver);
640
         closeStartGroupingElements(groupingFragment);
641
         openStartElement(xPathFragment, namespaceResolver);
642
         endElement(xPathFragment, namespaceResolver);
643
     }
644
    
645
    
646
     
647
     /**
648
      * This class will typically be used in conjunction with an XMLFragmentReader.
649
      * The XMLFragmentReader will walk a given XMLFragment node and report events
650
      * to this class - the event's data is then written to the enclosing class'
651
      * writer.
652
      *
653
      * @see org.eclipse.persistence.internal.oxm.record.XMLFragmentReader
654
      */
655
     protected class JsonObjectBuilderRecordContentHandler implements ExtendedContentHandler, LexicalHandler {
656
657
         JsonObjectBuilderRecordContentHandler() {
658
         }
659
660
         // --------------------- CONTENTHANDLER METHODS --------------------- //
661
         public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
662
                 XPathFragment xPathFragment = new XPathFragment(localName);
663
                 xPathFragment.setNamespaceURI(namespaceURI);
664
                 openStartElement(xPathFragment, namespaceResolver);
665
                 handleAttributes(atts);
666
         }
667
668
         public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
669
             XPathFragment xPathFragment = new XPathFragment(localName);
670
             xPathFragment.setNamespaceURI(namespaceURI);
671
             
672
             JsonObjectBuilderWriterRecord.this.endElement(xPathFragment, namespaceResolver);        
673
         }
674
675
         public void startPrefixMapping(String prefix, String uri) throws SAXException {
676
         }
677
678
         public void characters(char[] ch, int start, int length) throws SAXException {
679
             String characters = new String (ch, start, length);
680
             characters(characters);
681
         }
682
683
         public void characters(CharSequence characters) throws SAXException {           
684
             JsonObjectBuilderWriterRecord.this.characters(characters.toString());      
685
         }
686
687
         // --------------------- LEXICALHANDLER METHODS --------------------- //
688
         public void comment(char[] ch, int start, int length) throws SAXException {
689
         }
690
691
         public void startCDATA() throws SAXException {
692
         }
693
694
         public void endCDATA() throws SAXException {
695
         }
696
697
         // --------------------- CONVENIENCE METHODS --------------------- //
698
            protected void handleAttributes(Attributes atts) {
699
             for (int i=0, attsLength = atts.getLength(); i<attsLength; i++) {
700
                 String qName = atts.getQName(i);
701
                 if((qName != null && (qName.startsWith(javax.xml.XMLConstants.XMLNS_ATTRIBUTE + Constants.COLON) || qName.equals(javax.xml.XMLConstants.XMLNS_ATTRIBUTE)))) {
702
                     continue;
703
                 }
704
                 attribute(atts.getURI(i), atts.getLocalName(i), qName, atts.getValue(i));
705
             }
706
         }
707
708
         protected void writeComment(char[] chars, int start, int length) {        
709
         }
710
        
711
         protected void writeCharacters(char[] chars, int start, int length) {
712
             try {
713
                 characters(chars, start, length);
714
             } catch (SAXException e) {
715
                 throw XMLMarshalException.marshalException(e);
716
             }           
717
         }
718
         // --------------- SATISFY CONTENTHANDLER INTERFACE --------------- //
719
         public void endPrefixMapping(String prefix) throws SAXException {}
720
         public void processingInstruction(String target, String data) throws SAXException {}
721
         public void setDocumentLocator(Locator locator) {}
722
         public void startDocument() throws SAXException {}
723
         public void endDocument() throws SAXException {}
724
         public void skippedEntity(String name) throws SAXException {}
725
         public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {}
726
727
         // --------------- SATISFY LEXICALHANDLER INTERFACE --------------- //
728
         public void startEntity(String name) throws SAXException {}
729
         public void endEntity(String name) throws SAXException {}
730
         public void startDTD(String name, String publicId, String systemId) throws SAXException {}
731
         public void endDTD() throws SAXException {}
732
         @Override
733
         public void setNil(boolean isNil) {}
734
735
     }
736
737
     
738
     /**
739
     * Instances of this class are used to maintain state about the current
740
     * level of the JSON message being marshalled.
741
     */
742
    protected static class Level {
743
        
744
        private boolean isCollection;
745
        private boolean emptyCollection;
746
        private String keyName;        
747
        private JsonObjectBuilder jsonObjectBuilder;
748
        private JsonArrayBuilder jsonArrayBuilder;
749
        private boolean isComplex;
750
        private Level parentLevel;
751
        
752
        public Level(boolean isCollection, Level parentLevel) {
753
            setCollection(isCollection);
754
            emptyCollection = true;
755
            this.parentLevel = parentLevel;
756
        }
757
758
        public boolean isCollection() {
759
            return isCollection;
760
        }
761
762
        public void setCollection(boolean isCollection) {
763
            this.isCollection = isCollection;
764
            if(isCollection && jsonArrayBuilder == null){
765
                jsonArrayBuilder = Json.createArrayBuilder();
766
            }
767
        }
768
769
        public String getKeyName() {
770
            return keyName;
771
        }
772
773
        public void setKeyName(String keyName) {
774
            this.keyName = keyName;
775
        }
776
777
        public JsonObjectBuilder getJsonObjectBuilder() {
778
            return jsonObjectBuilder;
779
        }
780
781
        public void setJsonObjectBuilder(JsonObjectBuilder jsonObjectBuilder) {
782
            this.jsonObjectBuilder = jsonObjectBuilder;
783
        }
784
785
        public JsonArrayBuilder getJsonArrayBuilder() {
786
            return jsonArrayBuilder;
787
        }
788
789
        public void setJsonArrayBuilder(JsonArrayBuilder jsonArrayBuilder) {
790
            this.jsonArrayBuilder = jsonArrayBuilder;
791
        }
792
793
        public boolean isEmptyCollection() {
794
            return emptyCollection;
795
        }
796
797
        public void setEmptyCollection(boolean emptyCollection) {
798
            this.emptyCollection = emptyCollection;
799
        }
800
        public boolean isComplex() {
801
            return isComplex;
802
        }
803
804
        public void setComplex(boolean isComplex) {
805
            this.isComplex = isComplex;
806
            if(isComplex && jsonObjectBuilder == null){
807
                jsonObjectBuilder = Json.createObjectBuilder();
808
            }
809
        }
810
811
    }
812
813
}

Return to bug 411377