001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package java.beans;
019:
020: import java.awt.SystemColor;
021: import java.awt.font.TextAttribute;
022: import java.io.OutputStream;
023: import java.io.OutputStreamWriter;
024: import java.io.PrintWriter;
025: import java.io.UnsupportedEncodingException;
026: import java.lang.reflect.Array;
027: import java.lang.reflect.Proxy;
028: import java.util.ArrayList;
029: import java.util.Collections;
030: import java.util.Iterator;
031: import java.util.List;
032:
033: /**
034: * <code>XMLEncoder</code> exnteds <code>Encoder</code> to write out the
035: * encoded statements and expressions in xml format. The xml can be read by
036: * <code>XMLDecoder</code> later to restore objects and their states.
037: * <p>
038: * The API is similar to <code>ObjectOutputStream</code>.
039: * </p>
040: *
041: */
042: public class XMLEncoder extends Encoder {
043:
044: /*
045: * Every object written by the encoder has a record.
046: */
047: private static class Record {
048: boolean born = false;
049:
050: // The expression by which the object is created or obtained.
051: Expression exp = null;
052:
053: // Id of the object, if it is referenced more than once.
054: String id = null;
055:
056: // Count of the references of the object.
057: int refCount = 0;
058:
059: // A list of statements that execute on the object.
060: ArrayList<Statement> stats = new ArrayList<Statement>();
061: }
062:
063: private static final int INDENT_UNIT = 1;
064:
065: private static final boolean isStaticConstantsSupported = true;
066:
067: // the main record of all root objects
068: private ArrayList<Object> flushPending = new ArrayList<Object>();
069:
070: // the record of root objects with a void tag
071: private ArrayList<Object> flushPendingStat = new ArrayList<Object>();
072:
073: // keep the pre-required objects for each root object
074: private ArrayList<Object> flushPrePending = new ArrayList<Object>();
075:
076: private boolean hasXmlHeader = false;
077:
078: private int idSerialNo = 0;
079:
080: /*
081: * if any expression or statement references owner, it is set true in method
082: * recordStatement() or recordExpressions(), and, at the first time
083: * flushObject() meets an owner object, it calls the flushOwner() method and
084: * then set needOwner to false, so that all succeeding flushing of owner
085: * will call flushExpression() or flushStatement() normally, which will get
086: * a reference of the owner property.
087: */
088: private boolean needOwner = false;
089:
090: private PrintWriter out;
091:
092: private Object owner = null;
093:
094: private ReferenceMap records = new ReferenceMap();
095:
096: private boolean writingObject = false;
097:
098: /**
099: * Construct a <code>XMLEncoder</code>.
100: *
101: * @param out
102: * the output stream where xml is writtern to
103: */
104: public XMLEncoder(OutputStream out) {
105: if (null != out) {
106: try {
107: this .out = new PrintWriter(new OutputStreamWriter(out,
108: "UTF-8"), true); //$NON-NLS-1$
109: } catch (UnsupportedEncodingException e) {
110: // never occur
111: e.printStackTrace();
112: }
113: }
114: }
115:
116: /**
117: * Call <code>flush()</code> first, then write out xml footer and close
118: * the underlying output stream.
119: */
120: public void close() {
121: flush();
122: out.println("</java>"); //$NON-NLS-1$
123: out.close();
124: }
125:
126: private StringBuffer decapitalize(String s) {
127: StringBuffer buf = new StringBuffer(s);
128: buf.setCharAt(0, Character.toLowerCase(buf.charAt(0)));
129: return buf;
130: }
131:
132: /**
133: * Writes out all objects since last flush to the output stream.
134: * <p>
135: * The implementation write the xml header first if it has not been
136: * writtern. Then all pending objects since last flush are writtern.
137: * </p>
138: */
139: @SuppressWarnings("nls")
140: public void flush() {
141: synchronized (this ) {
142: // write xml header
143: if (!hasXmlHeader) {
144: out
145: .println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
146: out.println("<java version=\""
147: + System.getProperty("java.version")
148: + "\" class=\"java.beans.XMLDecoder\">");
149: hasXmlHeader = true;
150: }
151:
152: // preprocess pending objects
153: for (Iterator<Object> iter = flushPending.iterator(); iter
154: .hasNext();) {
155: Object o = iter.next();
156: Record rec = (Record) records.get(o);
157: if (rec != null) {
158: preprocess(o, rec);
159: }
160: }
161:
162: // flush pending objects
163: for (Iterator<Object> iter = flushPending.iterator(); iter
164: .hasNext();) {
165: Object o = iter.next();
166: flushObject(o, INDENT_UNIT);
167: // remove flushed obj
168: iter.remove();
169: }
170:
171: // clear statement records
172: records.clear();
173: flushPendingStat.clear();
174:
175: // remove all old->new mappings
176: super .clear();
177: }
178: }
179:
180: @SuppressWarnings("nls")
181: private void flushBasicObject(Object obj, int indent) {
182: if (obj instanceof Proxy) {
183: return;
184: }
185: flushIndent(indent);
186: if (obj == null) {
187: out.println("<null />");
188: } else if (obj instanceof String) {
189: Record rec = (Record) records.get(obj);
190: if (null != rec) {
191: if (flushPendingStat.contains(obj)) {
192: flushExpression(obj, rec, indent - 3, true);
193: } else {
194: flushExpression(obj, rec, indent - 3, false);
195: }
196: return;
197: }
198: out.print("<string>");
199: flushString((String) obj);
200: out.println("</string>");
201: } else if (obj instanceof Class) {
202: out.print("<class>");
203: out.print(((Class) obj).getName());
204: out.println("</class>");
205: } else if (obj instanceof Boolean) {
206: out.print("<boolean>");
207: out.print(obj);
208: out.println("</boolean>");
209: } else if (obj instanceof Byte) {
210: out.print("<byte>");
211: out.print(obj);
212: out.println("</byte>");
213: } else if (obj instanceof Character) {
214: out.print("<char>");
215: out.print(obj);
216: out.println("</char>");
217: } else if (obj instanceof Double) {
218: out.print("<double>");
219: out.print(obj);
220: out.println("</double>");
221: } else if (obj instanceof Float) {
222: out.print("<float>");
223: out.print(obj);
224: out.println("</float>");
225: } else if (obj instanceof Integer) {
226: out.print("<int>");
227: out.print(obj);
228: out.println("</int>");
229: } else if (obj instanceof Long) {
230: out.print("<long>");
231: out.print(obj);
232: out.println("</long>");
233: } else if (obj instanceof Short) {
234: out.print("<short>");
235: out.print(obj);
236: out.println("</short>");
237: } else {
238: getExceptionListener().exceptionThrown(
239: new Exception("Unknown basic object: " + obj));
240: }
241: }
242:
243: @SuppressWarnings("nls")
244: private void flushExpression(Object obj, Record rec, int indent,
245: boolean asStatement) {
246: // not first time, use idref
247: if (rec.id != null) {
248: flushIndent(indent);
249: out.print("<object idref=\"");
250: out.print(rec.id);
251: out.println("\" />");
252: return;
253: }
254:
255: // generate id, if necessary
256: if (rec.refCount > 1) {
257: rec.id = nameForClass(obj.getClass()) + idSerialNo;
258: idSerialNo++;
259: }
260:
261: // flush
262: Statement stat = asStatement ? new Statement(rec.exp
263: .getTarget(), rec.exp.getMethodName(), rec.exp
264: .getArguments()) : rec.exp;
265: flushStatement(stat, rec.id, rec.stats, indent);
266: }
267:
268: private void flushIndent(int indent) {
269: for (int i = 0; i < indent; i++) {
270: out.print(" "); //$NON-NLS-1$
271: }
272: }
273:
274: private void flushObject(Object obj, int indent) {
275: Record rec = (Record) records.get(obj);
276: if (rec == null && !isBasicType(obj))
277: return;
278:
279: if (obj == owner && this .needOwner) {
280: flushOwner(obj, rec, indent);
281: this .needOwner = false;
282: return;
283: }
284:
285: if (isBasicType(obj)) {
286: flushBasicObject(obj, indent);
287: } else {
288: if (flushPendingStat.contains(obj)) {
289: flushExpression(obj, rec, indent, true);
290: } else {
291: flushExpression(obj, rec, indent, false);
292: }
293: }
294: }
295:
296: @SuppressWarnings("nls")
297: private void flushOwner(Object obj, Record rec, int indent) {
298: if (rec.refCount > 1) {
299: rec.id = nameForClass(obj.getClass()) + idSerialNo;
300: idSerialNo++;
301: }
302:
303: flushIndent(indent);
304: String tagName = "void";
305: out.print("<");
306: out.print(tagName);
307:
308: // id attribute
309: if (rec.id != null) {
310: out.print(" id=\"");
311: out.print(rec.id);
312: out.print("\"");
313: }
314:
315: out.print(" property=\"owner\"");
316:
317: // open tag, end
318: if (rec.exp.getArguments().length == 0 && rec.stats.isEmpty()) {
319: out.println("/>");
320: return;
321: }
322: out.println(">");
323:
324: // arguments
325: for (int i = 0; i < rec.exp.getArguments().length; i++) {
326: flushObject(rec.exp.getArguments()[i], indent + INDENT_UNIT);
327: }
328:
329: // sub statements
330: flushSubStatements(rec.stats, indent);
331:
332: // close tag
333: flushIndent(indent);
334: out.print("</");
335: out.print(tagName);
336: out.println(">");
337: }
338:
339: @SuppressWarnings("nls")
340: private void flushStatArray(Statement stat, String id,
341: List<?> subStats, int indent) {
342: // open tag, begin
343: flushIndent(indent);
344: out.print("<array");
345:
346: // id attribute
347: if (id != null) {
348: out.print(" id=\"");
349: out.print(id);
350: out.print("\"");
351: }
352:
353: // class & length
354: out.print(" class=\"");
355: out.print(((Class) stat.getArguments()[0]).getName());
356: out.print("\" length=\"");
357: out.print(stat.getArguments()[1]);
358: out.print("\"");
359:
360: // open tag, end
361: if (subStats.isEmpty()) {
362: out.println("/>");
363: return;
364: }
365: out.println(">");
366:
367: // sub statements
368: flushSubStatements(subStats, indent);
369:
370: // close tag
371: flushIndent(indent);
372: out.println("</array>");
373: }
374:
375: @SuppressWarnings("nls")
376: private void flushStatCommon(Statement stat, String id,
377: List<?> subStats, int indent) {
378: // open tag, begin
379: flushIndent(indent);
380: String tagName = stat instanceof Expression ? "object" : "void";
381: out.print("<");
382: out.print(tagName);
383:
384: // id attribute
385: if (id != null) {
386: out.print(" id=\"");
387: out.print(id);
388: out.print("\"");
389: }
390:
391: // special class attribute
392: if (stat.getTarget() instanceof Class) {
393: out.print(" class=\"");
394: out.print(((Class) stat.getTarget()).getName());
395: out.print("\"");
396: }
397:
398: // method attribute
399: if (!"new".equals(stat.getMethodName())) {
400: out.print(" method=\"");
401: out.print(stat.getMethodName());
402: out.print("\"");
403: }
404:
405: // open tag, end
406: if (stat.getArguments().length == 0 && subStats.isEmpty()) {
407: out.println("/>");
408: return;
409: }
410: out.println(">");
411:
412: // arguments
413: for (int i = 0; i < stat.getArguments().length; i++) {
414: flushObject(stat.getArguments()[i], indent + INDENT_UNIT);
415: }
416:
417: // sub statements
418: flushSubStatements(subStats, indent);
419:
420: // close tag
421: flushIndent(indent);
422: out.print("</");
423: out.print(tagName);
424: out.println(">");
425: }
426:
427: @SuppressWarnings("nls")
428: private void flushStatement(Statement stat, String id,
429: List<?> subStats, int indent) {
430: Object target = stat.getTarget();
431: String method = stat.getMethodName();
432: Object args[] = stat.getArguments();
433:
434: // special case for array
435: if (Array.class == target && "newInstance".equals(method)) {
436: flushStatArray(stat, id, subStats, indent);
437: return;
438: }
439: // special case for get(int) and set(int, Object)
440: if (isGetArrayStat(target, method, args)
441: || isSetArrayStat(target, method, args)) {
442: flushStatIndexed(stat, id, subStats, indent);
443: return;
444: }
445: // special case for getProperty() and setProperty(Object)
446: if (isGetPropertyStat(method, args)
447: || isSetPropertyStat(method, args)) {
448: flushStatGetterSetter(stat, id, subStats, indent);
449: return;
450: }
451:
452: if (isStaticConstantsSupported
453: && "getField".equals(stat.getMethodName())) {
454: flushStatField(stat, id, indent);
455: return;
456: }
457:
458: // common case
459: flushStatCommon(stat, id, subStats, indent);
460: }
461:
462: @SuppressWarnings("nls")
463: private void flushStatField(Statement stat, String id, int indent) {
464: // open tag, begin
465: flushIndent(indent);
466: String tagName = "object";
467: out.print("<");
468: out.print(tagName);
469:
470: // id attribute
471: if (id != null) {
472: out.print(" id=\"");
473: out.print(id);
474: out.print("\"");
475: }
476:
477: // special class attribute
478: if (stat.getTarget() instanceof Class) {
479: out.print(" class=\"");
480: out.print(((Class) stat.getTarget()).getName());
481: out.print("\"");
482: }
483:
484: Object target = stat.getTarget();
485: if (target == SystemColor.class
486: || target == TextAttribute.class) {
487: out.print(" field=\"");
488: out.print(stat.getArguments()[0]);
489: out.print("\"");
490: out.println("/>");
491:
492: } else {
493: out.print(" method=\"");
494: out.print(stat.getMethodName());
495: out.print("\"");
496: out.println(">");
497: Object fieldName = stat.getArguments()[0];
498: flushObject(fieldName, indent + INDENT_UNIT);
499: flushIndent(indent);
500: out.println("</object>");
501: }
502: }
503:
504: @SuppressWarnings("nls")
505: private void flushStatGetterSetter(Statement stat, String id,
506: List<?> subStats, int indent) {
507: // open tag, begin
508: flushIndent(indent);
509: String tagName = stat instanceof Expression ? "object" : "void";
510: out.print("<");
511: out.print(tagName);
512:
513: // id attribute
514: if (id != null) {
515: out.print(" id=\"");
516: out.print(id);
517: out.print("\"");
518: }
519:
520: // special class attribute
521: if (stat.getTarget() instanceof Class) {
522: out.print(" class=\"");
523: out.print(((Class) stat.getTarget()).getName());
524: out.print("\"");
525: }
526:
527: // property attribute
528: out.print(" property=\"");
529: out.print(decapitalize(stat.getMethodName().substring(3)));
530: out.print("\"");
531:
532: // open tag, end
533: if (stat.getArguments().length == 0 && subStats.isEmpty()) {
534: out.println("/>");
535: return;
536: }
537: out.println(">");
538:
539: // arguments
540: for (int i = 0; i < stat.getArguments().length; i++) {
541: flushObject(stat.getArguments()[i], indent + INDENT_UNIT);
542: }
543:
544: // sub statements
545: flushSubStatements(subStats, indent);
546:
547: // close tag
548: flushIndent(indent);
549: out.print("</");
550: out.print(tagName);
551: out.println(">");
552: }
553:
554: @SuppressWarnings("nls")
555: private void flushStatIndexed(Statement stat, String id,
556: List<?> subStats, int indent) {
557: // open tag, begin
558: flushIndent(indent);
559: String tagName = stat instanceof Expression ? "object" : "void";
560: out.print("<");
561: out.print(tagName);
562:
563: // id attribute
564: if (id != null) {
565: out.print(" id=\"");
566: out.print(id);
567: out.print("\"");
568: }
569:
570: // special class attribute
571: if (stat.getTarget() instanceof Class) {
572: out.print(" class=\"");
573: out.print(((Class) stat.getTarget()).getName());
574: out.print("\"");
575: }
576:
577: // index attribute
578: out.print(" index=\"");
579: out.print(stat.getArguments()[0]);
580: out.print("\"");
581:
582: // open tag, end
583: if (stat.getArguments().length == 1 && subStats.isEmpty()) {
584: out.println("/>");
585: return;
586: }
587: out.println(">");
588:
589: // arguments
590: for (int i = 1; i < stat.getArguments().length; i++) {
591: flushObject(stat.getArguments()[i], indent + INDENT_UNIT);
592: }
593:
594: // sub statements
595: flushSubStatements(subStats, indent);
596:
597: // close tag
598: flushIndent(indent);
599: out.print("</");
600: out.print(tagName);
601: out.println(">");
602: }
603:
604: @SuppressWarnings("nls")
605: private void flushString(String s) {
606: char c;
607: for (int i = 0; i < s.length(); i++) {
608: c = s.charAt(i);
609: if (c == '<') {
610: out.print("<");
611: } else if (c == '>') {
612: out.print(">");
613: } else if (c == '&') {
614: out.print("&");
615: } else if (c == '\'') {
616: out.print("'");
617: } else if (c == '"') {
618: out.print(""");
619: } else {
620: out.print(c);
621: }
622: }
623: }
624:
625: private void flushSubStatements(List<?> subStats, int indent) {
626: for (int i = 0; i < subStats.size(); i++) {
627: Statement subStat = (Statement) subStats.get(i);
628: try {
629: if (subStat instanceof Expression) {
630: Expression subExp = (Expression) subStat;
631: Object obj = subExp.getValue();
632: Record rec = (Record) records.get(obj);
633: flushExpression(obj, rec, indent + INDENT_UNIT,
634: true);
635: } else {
636: flushStatement(subStat, null,
637: Collections.EMPTY_LIST, indent
638: + INDENT_UNIT);
639: }
640: } catch (Exception e) {
641: // should not happen
642: getExceptionListener().exceptionThrown(e);
643: }
644: }
645: }
646:
647: /**
648: * Returns the owner of this encoder.
649: *
650: * @return the owner of this encoder
651: */
652: public Object getOwner() {
653: return owner;
654: }
655:
656: private boolean isBasicType(Object value) {
657: return value == null || value instanceof Boolean
658: || value instanceof Byte || value instanceof Character
659: || value instanceof Class || value instanceof Double
660: || value instanceof Float || value instanceof Integer
661: || value instanceof Long || value instanceof Short
662: || value instanceof String || value instanceof Proxy;
663: }
664:
665: private boolean isGetArrayStat(Object target, String method,
666: Object[] args) {
667: return ("get".equals(method) && args.length == 1 //$NON-NLS-1$
668: && args[0] instanceof Integer && target.getClass()
669: .isArray());
670: }
671:
672: private boolean isGetPropertyStat(String method, Object[] args) {
673: return (method.startsWith("get") && method.length() > 3 && args.length == 0); //$NON-NLS-1$
674: }
675:
676: private boolean isSetArrayStat(Object target, String method,
677: Object[] args) {
678: return ("set".equals(method) && args.length == 2 //$NON-NLS-1$
679: && args[0] instanceof Integer && target.getClass()
680: .isArray());
681: }
682:
683: private boolean isSetPropertyStat(String method, Object[] args) {
684: return (method.startsWith("set") && method.length() > 3 && args.length == 1); //$NON-NLS-1$
685: }
686:
687: private String nameForClass(Class<?> c) {
688: if (c.isArray()) {
689: return nameForClass(c.getComponentType()) + "Array"; //$NON-NLS-1$
690: }
691: String name = c.getName();
692: int i = name.lastIndexOf('.');
693: if (-1 == i) {
694: return name;
695: }
696: return name.substring(i + 1);
697: }
698:
699: /*
700: * The preprocess removes unused statements and counts references of every
701: * object
702: */
703: private void preprocess(Object obj, Record rec) {
704: if (isBasicType(obj) && writingObject) {
705: return;
706: }
707:
708: // count reference
709: rec.refCount++;
710:
711: // do things only one time for each record
712: if (rec.refCount > 1) {
713: return;
714: }
715:
716: // deal with 'field' property
717: try {
718: if (isStaticConstantsSupported
719: && "getField".equals(((Record) records.get(rec.exp //$NON-NLS-1$
720: .getTarget())).exp.getMethodName())) {
721: records.remove(obj);
722: }
723: } catch (NullPointerException e) {
724: // do nothing, safely
725: }
726:
727: // do it recursively
728: if (null != rec.exp) {
729: Object args[] = rec.exp.getArguments();
730: for (int i = 0; i < args.length; i++) {
731: Record argRec = (Record) records.get(args[i]);
732: if (argRec != null) {
733: preprocess(args[i], argRec);
734: }
735: }
736: }
737:
738: for (Iterator<?> iter = rec.stats.iterator(); iter.hasNext();) {
739: Statement subStat = (Statement) iter.next();
740: if (subStat instanceof Expression) {
741: try {
742: Expression subExp = (Expression) subStat;
743: Record subRec = (Record) records.get(subExp
744: .getValue());
745: if (subRec == null || subRec.exp == null
746: || subRec.exp != subExp) {
747: iter.remove();
748: continue;
749: }
750: preprocess(subExp.getValue(), subRec);
751: if (subRec.stats.isEmpty()) {
752: if (isGetArrayStat(subExp.getTarget(), subExp
753: .getMethodName(), subExp.getArguments())
754: || isGetPropertyStat(subExp
755: .getMethodName(), subExp
756: .getArguments())) {
757: iter.remove();
758: continue;
759: }
760: }
761: } catch (Exception e) {
762: getExceptionListener().exceptionThrown(e);
763: iter.remove();
764: }
765: continue;
766: }
767:
768: Object subStatArgs[] = subStat.getArguments();
769: for (int i = 0; i < subStatArgs.length; i++) {
770: Record argRec = (Record) records.get(subStatArgs[i]);
771: if (argRec != null) {
772: preprocess(subStatArgs[i], argRec);
773: }
774: }
775: }
776: }
777:
778: private void recordExpression(Object value, Expression exp) {
779: // record how a new object is created or obtained
780: Record rec = (Record) records.get(value);
781: if (rec == null) {
782: rec = new Record();
783: records.put(value, rec);
784: }
785:
786: if (rec.exp == null) {
787: // it is generated by its sub stats
788: for (Iterator<?> iter = rec.stats.iterator(); iter
789: .hasNext();) {
790: Statement stat = (Statement) iter.next();
791: try {
792: if (stat instanceof Expression) {
793: flushPrePending.add(value);
794: }
795: } catch (Exception e) {
796: e.printStackTrace();
797: }
798:
799: }
800: }
801:
802: rec.exp = exp;
803:
804: // deal with 'owner' property
805: if (value == owner && owner != null) {
806: needOwner = true;
807: }
808:
809: // also record as a statement
810: recordStatement(exp);
811: }
812:
813: private void recordStatement(Statement stat) {
814: // deal with 'owner' property
815: if (stat.getTarget() == owner && owner != null) {
816: needOwner = true;
817: }
818:
819: // record how a statement affects the target object
820: Record rec = (Record) records.get(stat.getTarget());
821: if (rec == null) {
822: rec = new Record();
823: records.put(stat.getTarget(), rec);
824: }
825: rec.stats.add(stat);
826: }
827:
828: /**
829: * Sets the owner of this encoder.
830: *
831: * @param owner
832: * the owner to set
833: */
834: public void setOwner(Object owner) {
835: this .owner = owner;
836: }
837:
838: /**
839: * Records the expression so that it can be writtern out later, then calls
840: * super implementation.
841: */
842: @Override
843: public void writeExpression(Expression oldExp) {
844: boolean oldWritingObject = writingObject;
845: writingObject = true;
846: // get expression value
847: Object oldValue = null;
848: try {
849: oldValue = oldExp.getValue();
850: } catch (Exception e) {
851: getExceptionListener().exceptionThrown(
852: new Exception("failed to execute expression: " //$NON-NLS-1$
853: + oldExp, e));
854: return;
855: }
856:
857: // check existence
858: if (get(oldValue) != null
859: && (!(oldValue instanceof String) || oldWritingObject)) {
860: return;
861: }
862:
863: // record how the object is obtained
864: if (!isBasicType(oldValue)
865: || (oldValue instanceof String && !oldWritingObject)) {
866: recordExpression(oldValue, oldExp);
867: }
868:
869: super .writeExpression(oldExp);
870: writingObject = oldWritingObject;
871: }
872:
873: /**
874: * Records the object so that it can be writtern out later, then calls super
875: * implementation.
876: */
877: @Override
878: public void writeObject(Object o) {
879: synchronized (this ) {
880: boolean oldWritingObject = writingObject;
881: writingObject = true;
882: try {
883: super .writeObject(o);
884: } finally {
885: writingObject = oldWritingObject;
886: }
887:
888: // root object?
889: if (!writingObject) {
890: // add to pending
891: flushPending.addAll(flushPrePending);
892: flushPendingStat.addAll(flushPrePending);
893: flushPrePending.clear();
894: if (flushPending.contains(o)) {
895: flushPrePending.remove(o);
896: flushPendingStat.remove(o);
897: } else {
898: flushPending.add(o);
899: }
900: if (needOwner) {
901: this .flushPending.remove(owner);
902: this .flushPending.add(0, owner);
903: }
904: }
905: }
906: }
907:
908: /**
909: * Records the statement so that it can be writtern out later, then calls
910: * super implementation.
911: */
912: @Override
913: public void writeStatement(Statement oldStat) {
914: // record how the object is changed
915: recordStatement(oldStat);
916:
917: super.writeStatement(oldStat);
918: }
919:
920: }
|