Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.desktop/share/classes/java/beans/XMLEncoder.java
41152 views
1
/*
2
* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
package java.beans;
26
27
import java.io.*;
28
import java.util.*;
29
import java.lang.reflect.*;
30
import java.nio.charset.Charset;
31
import java.nio.charset.CharsetEncoder;
32
import java.nio.charset.IllegalCharsetNameException;
33
import java.nio.charset.UnsupportedCharsetException;
34
35
/**
36
* The {@code XMLEncoder} class is a complementary alternative to
37
* the {@code ObjectOutputStream} and can used to generate
38
* a textual representation of a <em>JavaBean</em> in the same
39
* way that the {@code ObjectOutputStream} can
40
* be used to create binary representation of {@code Serializable}
41
* objects. For example, the following fragment can be used to create
42
* a textual representation the supplied <em>JavaBean</em>
43
* and all its properties:
44
* <pre>
45
* XMLEncoder e = new XMLEncoder(
46
* new BufferedOutputStream(
47
* new FileOutputStream("Test.xml")));
48
* e.writeObject(new JButton("Hello, world"));
49
* e.close();
50
* </pre>
51
* Despite the similarity of their APIs, the {@code XMLEncoder}
52
* class is exclusively designed for the purpose of archiving graphs
53
* of <em>JavaBean</em>s as textual representations of their public
54
* properties. Like Java source files, documents written this way
55
* have a natural immunity to changes in the implementations of the classes
56
* involved. The {@code ObjectOutputStream} continues to be recommended
57
* for interprocess communication and general purpose serialization.
58
* <p>
59
* The {@code XMLEncoder} class provides a default denotation for
60
* <em>JavaBean</em>s in which they are represented as XML documents
61
* complying with version 1.0 of the XML specification and the
62
* UTF-8 character encoding of the Unicode/ISO 10646 character set.
63
* The XML documents produced by the {@code XMLEncoder} class are:
64
* <ul>
65
* <li>
66
* <em>Portable and version resilient</em>: they have no dependencies
67
* on the private implementation of any class and so, like Java source
68
* files, they may be exchanged between environments which may have
69
* different versions of some of the classes and between VMs from
70
* different vendors.
71
* <li>
72
* <em>Structurally compact</em>: The {@code XMLEncoder} class
73
* uses a <em>redundancy elimination</em> algorithm internally so that the
74
* default values of a Bean's properties are not written to the stream.
75
* <li>
76
* <em>Fault tolerant</em>: Non-structural errors in the file,
77
* caused either by damage to the file or by API changes
78
* made to classes in an archive remain localized
79
* so that a reader can report the error and continue to load the parts
80
* of the document which were not affected by the error.
81
* </ul>
82
* <p>
83
* Below is an example of an XML archive containing
84
* some user interface components from the <em>swing</em> toolkit:
85
* <pre>
86
* &lt;?xml version="1.0" encoding="UTF-8"?&gt;
87
* &lt;java version="1.0" class="java.beans.XMLDecoder"&gt;
88
* &lt;object class="javax.swing.JFrame"&gt;
89
* &lt;void property="name"&gt;
90
* &lt;string&gt;frame1&lt;/string&gt;
91
* &lt;/void&gt;
92
* &lt;void property="bounds"&gt;
93
* &lt;object class="java.awt.Rectangle"&gt;
94
* &lt;int&gt;0&lt;/int&gt;
95
* &lt;int&gt;0&lt;/int&gt;
96
* &lt;int&gt;200&lt;/int&gt;
97
* &lt;int&gt;200&lt;/int&gt;
98
* &lt;/object&gt;
99
* &lt;/void&gt;
100
* &lt;void property="contentPane"&gt;
101
* &lt;void method="add"&gt;
102
* &lt;object class="javax.swing.JButton"&gt;
103
* &lt;void property="label"&gt;
104
* &lt;string&gt;Hello&lt;/string&gt;
105
* &lt;/void&gt;
106
* &lt;/object&gt;
107
* &lt;/void&gt;
108
* &lt;/void&gt;
109
* &lt;void property="visible"&gt;
110
* &lt;boolean&gt;true&lt;/boolean&gt;
111
* &lt;/void&gt;
112
* &lt;/object&gt;
113
* &lt;/java&gt;
114
* </pre>
115
* The XML syntax uses the following conventions:
116
* <ul>
117
* <li>
118
* Each element represents a method call.
119
* <li>
120
* The "object" tag denotes an <em>expression</em> whose value is
121
* to be used as the argument to the enclosing element.
122
* <li>
123
* The "void" tag denotes a <em>statement</em> which will
124
* be executed, but whose result will not be used as an
125
* argument to the enclosing method.
126
* <li>
127
* Elements which contain elements use those elements as arguments,
128
* unless they have the tag: "void".
129
* <li>
130
* The name of the method is denoted by the "method" attribute.
131
* <li>
132
* XML's standard "id" and "idref" attributes are used to make
133
* references to previous expressions - so as to deal with
134
* circularities in the object graph.
135
* <li>
136
* The "class" attribute is used to specify the target of a static
137
* method or constructor explicitly; its value being the fully
138
* qualified name of the class.
139
* <li>
140
* Elements with the "void" tag are executed using
141
* the outer context as the target if no target is defined
142
* by a "class" attribute.
143
* <li>
144
* Java's String class is treated specially and is
145
* written &lt;string&gt;Hello, world&lt;/string&gt; where
146
* the characters of the string are converted to bytes
147
* using the UTF-8 character encoding.
148
* </ul>
149
* <p>
150
* Although all object graphs may be written using just these three
151
* tags, the following definitions are included so that common
152
* data structures can be expressed more concisely:
153
* <ul>
154
* <li>
155
* The default method name is "new".
156
* <li>
157
* A reference to a java class is written in the form
158
* &lt;class&gt;javax.swing.JButton&lt;/class&gt;.
159
* <li>
160
* Instances of the wrapper classes for Java's primitive types are written
161
* using the name of the primitive type as the tag. For example, an
162
* instance of the {@code Integer} class could be written:
163
* &lt;int&gt;123&lt;/int&gt;. Note that the {@code XMLEncoder} class
164
* uses Java's reflection package in which the conversion between
165
* Java's primitive types and their associated "wrapper classes"
166
* is handled internally. The API for the {@code XMLEncoder} class
167
* itself deals only with {@code Object}s.
168
* <li>
169
* In an element representing a nullary method whose name
170
* starts with "get", the "method" attribute is replaced
171
* with a "property" attribute whose value is given by removing
172
* the "get" prefix and decapitalizing the result.
173
* <li>
174
* In an element representing a monadic method whose name
175
* starts with "set", the "method" attribute is replaced
176
* with a "property" attribute whose value is given by removing
177
* the "set" prefix and decapitalizing the result.
178
* <li>
179
* In an element representing a method named "get" taking one
180
* integer argument, the "method" attribute is replaced
181
* with an "index" attribute whose value the value of the
182
* first argument.
183
* <li>
184
* In an element representing a method named "set" taking two arguments,
185
* the first of which is an integer, the "method" attribute is replaced
186
* with an "index" attribute whose value the value of the
187
* first argument.
188
* <li>
189
* A reference to an array is written using the "array"
190
* tag. The "class" and "length" attributes specify the
191
* sub-type of the array and its length respectively.
192
* </ul>
193
*
194
*<p>
195
* For more information you might also want to check out
196
* <a href="http://www.oracle.com/technetwork/java/persistence4-140124.html">
197
* Using XMLEncoder</a>,
198
* an article in <em>The Swing Connection.</em>
199
* @see XMLDecoder
200
* @see java.io.ObjectOutputStream
201
*
202
* @since 1.4
203
*
204
* @author Philip Milne
205
*/
206
public class XMLEncoder extends Encoder implements AutoCloseable {
207
208
private final CharsetEncoder encoder;
209
private final String charset;
210
private final boolean declaration;
211
212
private OutputStreamWriter out;
213
private Object owner;
214
private int indentation = 0;
215
private boolean internal = false;
216
private Map<Object, ValueData> valueToExpression;
217
private Map<Object, List<Statement>> targetToStatementList;
218
private boolean preambleWritten = false;
219
private NameGenerator nameGenerator;
220
221
private class ValueData {
222
public int refs = 0;
223
public boolean marked = false; // Marked -> refs > 0 unless ref was a target.
224
public String name = null;
225
public Expression exp = null;
226
}
227
228
/**
229
* Creates a new XML encoder to write out <em>JavaBeans</em>
230
* to the stream {@code out} using an XML encoding.
231
*
232
* @param out the stream to which the XML representation of
233
* the objects will be written
234
*
235
* @throws IllegalArgumentException
236
* if {@code out} is {@code null}
237
*
238
* @see XMLDecoder#XMLDecoder(InputStream)
239
*/
240
public XMLEncoder(OutputStream out) {
241
this(out, "UTF-8", true, 0);
242
}
243
244
/**
245
* Creates a new XML encoder to write out <em>JavaBeans</em>
246
* to the stream {@code out} using the given {@code charset}
247
* starting from the given {@code indentation}.
248
*
249
* @param out the stream to which the XML representation of
250
* the objects will be written
251
* @param charset the name of the requested charset;
252
* may be either a canonical name or an alias
253
* @param declaration whether the XML declaration should be generated;
254
* set this to {@code false}
255
* when embedding the contents in another XML document
256
* @param indentation the number of space characters to indent the entire XML document by
257
*
258
* @throws IllegalArgumentException
259
* if {@code out} or {@code charset} is {@code null},
260
* or if {@code indentation} is less than 0
261
*
262
* @throws IllegalCharsetNameException
263
* if {@code charset} name is illegal
264
*
265
* @throws UnsupportedCharsetException
266
* if no support for the named charset is available
267
* in this instance of the Java virtual machine
268
*
269
* @throws UnsupportedOperationException
270
* if loaded charset does not support encoding
271
*
272
* @see Charset#forName(String)
273
*
274
* @since 1.7
275
*/
276
public XMLEncoder(OutputStream out, String charset, boolean declaration, int indentation) {
277
if (out == null) {
278
throw new IllegalArgumentException("the output stream cannot be null");
279
}
280
if (indentation < 0) {
281
throw new IllegalArgumentException("the indentation must be >= 0");
282
}
283
Charset cs = Charset.forName(charset);
284
this.encoder = cs.newEncoder();
285
this.charset = charset;
286
this.declaration = declaration;
287
this.indentation = indentation;
288
this.out = new OutputStreamWriter(out, cs.newEncoder());
289
valueToExpression = new IdentityHashMap<>();
290
targetToStatementList = new IdentityHashMap<>();
291
nameGenerator = new NameGenerator();
292
}
293
294
/**
295
* Sets the owner of this encoder to {@code owner}.
296
*
297
* @param owner The owner of this encoder.
298
*
299
* @see #getOwner
300
*/
301
public void setOwner(Object owner) {
302
this.owner = owner;
303
writeExpression(new Expression(this, "getOwner", new Object[0]));
304
}
305
306
/**
307
* Gets the owner of this encoder.
308
*
309
* @return The owner of this encoder.
310
*
311
* @see #setOwner
312
*/
313
public Object getOwner() {
314
return owner;
315
}
316
317
/**
318
* Write an XML representation of the specified object to the output.
319
*
320
* @param o The object to be written to the stream.
321
*
322
* @see XMLDecoder#readObject
323
*/
324
public void writeObject(Object o) {
325
if (internal) {
326
super.writeObject(o);
327
}
328
else {
329
writeStatement(new Statement(this, "writeObject", new Object[]{o}));
330
}
331
}
332
333
private List<Statement> statementList(Object target) {
334
List<Statement> list = targetToStatementList.get(target);
335
if (list == null) {
336
list = new ArrayList<>();
337
targetToStatementList.put(target, list);
338
}
339
return list;
340
}
341
342
343
private void mark(Object o, boolean isArgument) {
344
if (o == null || o == this) {
345
return;
346
}
347
ValueData d = getValueData(o);
348
Expression exp = d.exp;
349
// Do not mark liternal strings. Other strings, which might,
350
// for example, come from resource bundles should still be marked.
351
if (o.getClass() == String.class && exp == null) {
352
return;
353
}
354
355
// Bump the reference counts of all arguments
356
if (isArgument) {
357
d.refs++;
358
}
359
if (d.marked) {
360
return;
361
}
362
d.marked = true;
363
Object target = exp.getTarget();
364
mark(exp);
365
if (!(target instanceof Class)) {
366
statementList(target).add(exp);
367
// Pending: Why does the reference count need to
368
// be incremented here?
369
d.refs++;
370
}
371
}
372
373
private void mark(Statement stm) {
374
Object[] args = stm.getArguments();
375
for (int i = 0; i < args.length; i++) {
376
Object arg = args[i];
377
mark(arg, true);
378
}
379
mark(stm.getTarget(), stm instanceof Expression);
380
}
381
382
383
/**
384
* Records the Statement so that the Encoder will
385
* produce the actual output when the stream is flushed.
386
* <P>
387
* This method should only be invoked within the context
388
* of initializing a persistence delegate.
389
*
390
* @param oldStm The statement that will be written
391
* to the stream.
392
* @see java.beans.PersistenceDelegate#initialize
393
*/
394
public void writeStatement(Statement oldStm) {
395
// System.out.println("XMLEncoder::writeStatement: " + oldStm);
396
boolean internal = this.internal;
397
this.internal = true;
398
try {
399
super.writeStatement(oldStm);
400
/*
401
Note we must do the mark first as we may
402
require the results of previous values in
403
this context for this statement.
404
Test case is:
405
os.setOwner(this);
406
os.writeObject(this);
407
*/
408
mark(oldStm);
409
Object target = oldStm.getTarget();
410
if (target instanceof Field) {
411
String method = oldStm.getMethodName();
412
Object[] args = oldStm.getArguments();
413
if ((method == null) || (args == null)) {
414
}
415
else if (method.equals("get") && (args.length == 1)) {
416
target = args[0];
417
}
418
else if (method.equals("set") && (args.length == 2)) {
419
target = args[0];
420
}
421
}
422
statementList(target).add(oldStm);
423
}
424
catch (Exception e) {
425
getExceptionListener().exceptionThrown(new Exception("XMLEncoder: discarding statement " + oldStm, e));
426
}
427
this.internal = internal;
428
}
429
430
431
/**
432
* Records the Expression so that the Encoder will
433
* produce the actual output when the stream is flushed.
434
* <P>
435
* This method should only be invoked within the context of
436
* initializing a persistence delegate or setting up an encoder to
437
* read from a resource bundle.
438
* <P>
439
* For more information about using resource bundles with the
440
* XMLEncoder, see
441
* <a href="http://www.oracle.com/technetwork/java/persistence4-140124.html#i18n">
442
* Creating Internationalized Applications</a>,
443
*
444
* @param oldExp The expression that will be written
445
* to the stream.
446
* @see java.beans.PersistenceDelegate#initialize
447
*/
448
public void writeExpression(Expression oldExp) {
449
boolean internal = this.internal;
450
this.internal = true;
451
Object oldValue = getValue(oldExp);
452
if (get(oldValue) == null || (oldValue instanceof String && !internal)) {
453
getValueData(oldValue).exp = oldExp;
454
super.writeExpression(oldExp);
455
}
456
this.internal = internal;
457
}
458
459
/**
460
* This method writes out the preamble associated with the
461
* XML encoding if it has not been written already and
462
* then writes out all of the values that been
463
* written to the stream since the last time {@code flush}
464
* was called. After flushing, all internal references to the
465
* values that were written to this stream are cleared.
466
*/
467
public void flush() {
468
if (!preambleWritten) { // Don't do this in constructor - it throws ... pending.
469
if (this.declaration) {
470
writeln("<?xml version=" + quote("1.0") +
471
" encoding=" + quote(this.charset) + "?>");
472
}
473
writeln("<java version=" + quote(System.getProperty("java.version")) +
474
" class=" + quote(XMLDecoder.class.getName()) + ">");
475
preambleWritten = true;
476
}
477
indentation++;
478
List<Statement> statements = statementList(this);
479
while (!statements.isEmpty()) {
480
Statement s = statements.remove(0);
481
if ("writeObject".equals(s.getMethodName())) {
482
outputValue(s.getArguments()[0], this, true);
483
}
484
else {
485
outputStatement(s, this, false);
486
}
487
}
488
indentation--;
489
490
Statement statement = getMissedStatement();
491
while (statement != null) {
492
outputStatement(statement, this, false);
493
statement = getMissedStatement();
494
}
495
496
try {
497
out.flush();
498
}
499
catch (IOException e) {
500
getExceptionListener().exceptionThrown(e);
501
}
502
clear();
503
}
504
505
void clear() {
506
super.clear();
507
nameGenerator.clear();
508
valueToExpression.clear();
509
targetToStatementList.clear();
510
}
511
512
Statement getMissedStatement() {
513
for (List<Statement> statements : this.targetToStatementList.values()) {
514
for (int i = 0; i < statements.size(); i++) {
515
if (Statement.class == statements.get(i).getClass()) {
516
return statements.remove(i);
517
}
518
}
519
}
520
return null;
521
}
522
523
524
/**
525
* This method calls {@code flush}, writes the closing
526
* postamble and then closes the output stream associated
527
* with this stream.
528
*/
529
public void close() {
530
flush();
531
writeln("</java>");
532
try {
533
out.close();
534
}
535
catch (IOException e) {
536
getExceptionListener().exceptionThrown(e);
537
}
538
}
539
540
private String quote(String s) {
541
return "\"" + s + "\"";
542
}
543
544
private ValueData getValueData(Object o) {
545
ValueData d = valueToExpression.get(o);
546
if (d == null) {
547
d = new ValueData();
548
valueToExpression.put(o, d);
549
}
550
return d;
551
}
552
553
/**
554
* Returns {@code true} if the argument,
555
* a Unicode code point, is valid in XML documents.
556
* Unicode characters fit into the low sixteen bits of a Unicode code point,
557
* and pairs of Unicode <em>surrogate characters</em> can be combined
558
* to encode Unicode code point in documents containing only Unicode.
559
* (The {@code char} datatype in the Java Programming Language
560
* represents Unicode characters, including unpaired surrogates.)
561
* <par>
562
* [2] Char ::= #x0009 | #x000A | #x000D
563
* | [#x0020-#xD7FF]
564
* | [#xE000-#xFFFD]
565
* | [#x10000-#x10ffff]
566
* </par>
567
*
568
* @param code the 32-bit Unicode code point being tested
569
* @return {@code true} if the Unicode code point is valid,
570
* {@code false} otherwise
571
*/
572
private static boolean isValidCharCode(int code) {
573
return (0x0020 <= code && code <= 0xD7FF)
574
|| (0x000A == code)
575
|| (0x0009 == code)
576
|| (0x000D == code)
577
|| (0xE000 <= code && code <= 0xFFFD)
578
|| (0x10000 <= code && code <= 0x10ffff);
579
}
580
581
private void writeln(String exp) {
582
try {
583
StringBuilder sb = new StringBuilder();
584
for(int i = 0; i < indentation; i++) {
585
sb.append(' ');
586
}
587
sb.append(exp);
588
sb.append('\n');
589
this.out.write(sb.toString());
590
}
591
catch (IOException e) {
592
getExceptionListener().exceptionThrown(e);
593
}
594
}
595
596
private void outputValue(Object value, Object outer, boolean isArgument) {
597
if (value == null) {
598
writeln("<null/>");
599
return;
600
}
601
602
if (value instanceof Class) {
603
writeln("<class>" + ((Class)value).getName() + "</class>");
604
return;
605
}
606
607
ValueData d = getValueData(value);
608
if (d.exp != null) {
609
Object target = d.exp.getTarget();
610
String methodName = d.exp.getMethodName();
611
612
if (target == null || methodName == null) {
613
throw new NullPointerException((target == null ? "target" :
614
"methodName") + " should not be null");
615
}
616
617
if (isArgument && target instanceof Field && methodName.equals("get")) {
618
Field f = (Field) target;
619
if (Modifier.isStatic(f.getModifiers())) {
620
writeln("<object class=" + quote(f.getDeclaringClass().getName()) +
621
" field=" + quote(f.getName()) + "/>");
622
return;
623
}
624
}
625
626
Class<?> primitiveType = primitiveTypeFor(value.getClass());
627
if (primitiveType != null && target == value.getClass() &&
628
methodName.equals("new")) {
629
String primitiveTypeName = primitiveType.getName();
630
// Make sure that character types are quoted correctly.
631
if (primitiveType == Character.TYPE) {
632
char code = ((Character) value).charValue();
633
if (!isValidCharCode(code)) {
634
writeln(createString(code));
635
return;
636
}
637
value = quoteCharCode(code);
638
if (value == null) {
639
value = Character.valueOf(code);
640
}
641
}
642
writeln("<" + primitiveTypeName + ">" + value + "</" +
643
primitiveTypeName + ">");
644
return;
645
}
646
647
} else if (value instanceof String) {
648
writeln(createString((String) value));
649
return;
650
}
651
652
if (d.name != null) {
653
if (isArgument) {
654
writeln("<object idref=" + quote(d.name) + "/>");
655
}
656
else {
657
outputXML("void", " idref=" + quote(d.name), value);
658
}
659
}
660
else if (d.exp != null) {
661
outputStatement(d.exp, outer, isArgument);
662
}
663
}
664
665
private static String quoteCharCode(int code) {
666
switch(code) {
667
case '&': return "&amp;";
668
case '<': return "&lt;";
669
case '>': return "&gt;";
670
case '"': return "&quot;";
671
case '\'': return "&apos;";
672
case '\r': return "&#13;";
673
default: return null;
674
}
675
}
676
677
private static String createString(int code) {
678
return "<char code=\"#" + Integer.toString(code, 16) + "\"/>";
679
}
680
681
private String createString(String string) {
682
StringBuilder sb = new StringBuilder();
683
sb.append("<string>");
684
int index = 0;
685
while (index < string.length()) {
686
int point = string.codePointAt(index);
687
int count = Character.charCount(point);
688
689
if (isValidCharCode(point) && this.encoder.canEncode(string.substring(index, index + count))) {
690
String value = quoteCharCode(point);
691
if (value != null) {
692
sb.append(value);
693
} else {
694
sb.appendCodePoint(point);
695
}
696
index += count;
697
} else {
698
sb.append(createString(string.charAt(index)));
699
index++;
700
}
701
}
702
sb.append("</string>");
703
return sb.toString();
704
}
705
706
private void outputStatement(Statement exp, Object outer, boolean isArgument) {
707
Object target = exp.getTarget();
708
String methodName = exp.getMethodName();
709
710
if (target == null || methodName == null) {
711
throw new NullPointerException((target == null ? "target" :
712
"methodName") + " should not be null");
713
}
714
715
Object[] args = exp.getArguments();
716
boolean expression = exp.getClass() == Expression.class;
717
Object value = (expression) ? getValue((Expression)exp) : null;
718
719
String tag = (expression && isArgument) ? "object" : "void";
720
String attributes = "";
721
ValueData d = getValueData(value);
722
723
// Special cases for targets.
724
if (target == outer) {
725
}
726
else if (target == Array.class && methodName.equals("newInstance")) {
727
tag = "array";
728
attributes = attributes + " class=" + quote(((Class)args[0]).getName());
729
attributes = attributes + " length=" + quote(args[1].toString());
730
args = new Object[]{};
731
}
732
else if (target.getClass() == Class.class) {
733
attributes = attributes + " class=" + quote(((Class)target).getName());
734
}
735
else {
736
d.refs = 2;
737
if (d.name == null) {
738
getValueData(target).refs++;
739
List<Statement> statements = statementList(target);
740
if (!statements.contains(exp)) {
741
statements.add(exp);
742
}
743
outputValue(target, outer, false);
744
}
745
if (expression) {
746
outputValue(value, outer, isArgument);
747
}
748
return;
749
}
750
if (expression && (d.refs > 1)) {
751
String instanceName = nameGenerator.instanceName(value);
752
d.name = instanceName;
753
attributes = attributes + " id=" + quote(instanceName);
754
}
755
756
// Special cases for methods.
757
if ((!expression && methodName.equals("set") && args.length == 2 &&
758
args[0] instanceof Integer) ||
759
(expression && methodName.equals("get") && args.length == 1 &&
760
args[0] instanceof Integer)) {
761
attributes = attributes + " index=" + quote(args[0].toString());
762
args = (args.length == 1) ? new Object[]{} : new Object[]{args[1]};
763
}
764
else if ((!expression && methodName.startsWith("set") && args.length == 1) ||
765
(expression && methodName.startsWith("get") && args.length == 0)) {
766
if (3 < methodName.length()) {
767
attributes = attributes + " property=" +
768
quote(Introspector.decapitalize(methodName.substring(3)));
769
}
770
}
771
else if (!methodName.equals("new") && !methodName.equals("newInstance")) {
772
attributes = attributes + " method=" + quote(methodName);
773
}
774
outputXML(tag, attributes, value, args);
775
}
776
777
private void outputXML(String tag, String attributes, Object value, Object... args) {
778
List<Statement> statements = statementList(value);
779
// Use XML's short form when there is no body.
780
if (args.length == 0 && statements.size() == 0) {
781
writeln("<" + tag + attributes + "/>");
782
return;
783
}
784
785
writeln("<" + tag + attributes + ">");
786
indentation++;
787
788
for(int i = 0; i < args.length; i++) {
789
outputValue(args[i], null, true);
790
}
791
792
while (!statements.isEmpty()) {
793
Statement s = statements.remove(0);
794
outputStatement(s, value, false);
795
}
796
797
indentation--;
798
writeln("</" + tag + ">");
799
}
800
801
@SuppressWarnings("rawtypes")
802
static Class primitiveTypeFor(Class wrapper) {
803
if (wrapper == Boolean.class) return Boolean.TYPE;
804
if (wrapper == Byte.class) return Byte.TYPE;
805
if (wrapper == Character.class) return Character.TYPE;
806
if (wrapper == Short.class) return Short.TYPE;
807
if (wrapper == Integer.class) return Integer.TYPE;
808
if (wrapper == Long.class) return Long.TYPE;
809
if (wrapper == Float.class) return Float.TYPE;
810
if (wrapper == Double.class) return Double.TYPE;
811
if (wrapper == Void.class) return Void.TYPE;
812
return null;
813
}
814
}
815
816