1 /* XMLEncoder.java
2  Copyright (C) 2004, 2005 Free Software Foundation, Inc.
3 
4  This file is part of GNU Classpath.
5 
6  GNU Classpath is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2, or (at your option)
9  any later version.
10 
11  GNU Classpath is distributed in the hope that it will be useful, but
12  WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with GNU Classpath; see the file COPYING.  If not, write to the
18  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19  02110-1301 USA.
20 
21  Linking this library statically or dynamically with other modules is
22  making a combined work based on this library.  Thus, the terms and
23  conditions of the GNU General Public License cover the whole
24  combination.
25 
26  As a special exception, the copyright holders of this library give you
27  permission to link this library with independent modules to produce an
28  executable, regardless of the license terms of these independent
29  modules, and to copy and distribute the resulting executable under
30  terms of your choice, provided that you also meet, for each linked
31  independent module, the terms and conditions of the license of that
32  module.  An independent module is a module which is not derived from
33  or based on this library.  If you modify this library, you may extend
34  this exception to your version of the library, but you are not
35  obligated to do so.  If you do not wish to do so, delete this
36  exception statement from your version. */
37 
38 
39 package java.beans;
40 
41 import gnu.java.beans.encoder.ScanEngine;
42 
43 import java.io.OutputStream;
44 
45 /**
46  * This class uses the {@link PersistenceDelegate} and {@link Encoder}
47  * infrastructure to generate an XML representation of the objects it
48  * serializes.
49  *
50  * @author Robert Schuster (robertschuster@fsfe.org)
51  * @since 1.4
52  */
53 public class XMLEncoder
54   extends Encoder
55   implements AutoCloseable
56 {
57   Object owner;
58 
59   Exception exception;
60 
61   ScanEngine scanEngine;
62 
63   private int accessCounter = 0;
64 
XMLEncoder(OutputStream os)65   public XMLEncoder(OutputStream os)
66   {
67     scanEngine = new ScanEngine(os);
68   }
69 
close()70   public void close()
71   {
72     if (scanEngine != null)
73       {
74         scanEngine.close();
75         scanEngine = null;
76       }
77   }
78 
flush()79   public void flush()
80   {
81     scanEngine.flush();
82   }
83 
writeExpression(Expression expr)84   public void writeExpression(Expression expr)
85   {
86     // Implementation note: Why is this method overwritten and nearly exactly
87     // reimplemented as in Encoder?
88     // The Encoder class can (and should be) subclassed by users outside of the
89     // java.beans package. While I have doubts that this is possible from an
90     // API design point of view I tried to replicate the Encoder's behavior
91     // in the JDK as exactly as possible. This strictness however made it
92     // extremely complicated to implement the XMLEncoder's backend. Therefore
93     // I decided to copy the Encoder's implementation and make all changes
94     // I needed for a succesfull operation of XMLEncoder.
95     //
96     // The same is true for the writeStatement method.
97 
98     //  Silently ignore out of bounds calls.
99     if (accessCounter <= 0)
100       return;
101 
102     scanEngine.writeExpression(expr);
103 
104 
105     Object target = expr.getTarget();
106     Object value = null;
107     Object newValue = null;
108 
109     try
110       {
111         value = expr.getValue();
112       }
113     catch (Exception e)
114       {
115         getExceptionListener().exceptionThrown(e);
116         return;
117       }
118 
119 
120     newValue = get(value);
121 
122     if (newValue == null)
123       {
124         Object newTarget = get(target);
125         if (newTarget == null)
126           {
127             writeObject(target);
128             newTarget = get(target);
129 
130             // May happen if exception was thrown.
131             if (newTarget == null)
132               {
133                 return;
134               }
135           }
136 
137         Object[] args = expr.getArguments();
138         Object[] newArgs = new Object[args.length];
139 
140         for (int i = 0; i < args.length; i++)
141           {
142             newArgs[i] = get(args[i]);
143             if (newArgs[i] == null || isImmutableType(args[i].getClass()))
144               {
145                 writeObject(args[i]);
146                 newArgs[i] = get(args[i]);
147               }
148           }
149 
150         Expression newExpr = new Expression(newTarget, expr.getMethodName(),
151                                             newArgs);
152 
153         // Fakes the result of Class.forName(<primitiveType>) to make it possible
154         // to hand such a type to the encoding process.
155         if (value instanceof Class && ((Class) value).isPrimitive())
156           newExpr.setValue(value);
157 
158         // Instantiates the new object.
159         try
160           {
161             newValue = newExpr.getValue();
162 
163             putCandidate(value, newValue);
164           }
165         catch (Exception e)
166           {
167             getExceptionListener().exceptionThrown(e);
168 
169             // In Statement.writeExpression we had no possibility to flags
170             // an erroneous state to the ScanEngine without behaving different
171             // to the JDK.
172             scanEngine.revoke();
173 
174             return;
175           }
176 
177         writeObject(value);
178 
179       }
180     else if(value.getClass() == String.class || value.getClass() == Class.class)
181       {
182         writeObject(value);
183       }
184 
185     scanEngine.end();
186   }
187 
writeStatement(Statement stmt)188   public void writeStatement(Statement stmt)
189   {
190     // In case of questions have a at the implementation note in
191     // writeExpression.
192 
193     scanEngine.writeStatement(stmt);
194 
195     //  Silently ignore out of bounds calls.
196     if (accessCounter <= 0)
197       return;
198 
199     Object target = stmt.getTarget();
200 
201     Object newTarget = get(target);
202     if (newTarget == null)
203       {
204         writeObject(target);
205         newTarget = get(target);
206       }
207 
208     Object[] args = stmt.getArguments();
209     Object[] newArgs = new Object[args.length];
210 
211     for (int i = 0; i < args.length; i++)
212       {
213         // Here is the difference to the original writeStatement
214         // method in Encoder. In case that the object is known or
215         // not an immutable we put it directly into the ScanEngine
216         // which will then generate an object reference for it.
217         newArgs[i] = get(args[i]);
218         if (newArgs[i] == null || isImmutableType(args[i].getClass()))
219           {
220             writeObject(args[i]);
221             newArgs[i] = get(args[i]);
222           }
223         else
224           scanEngine.writeObject(args[i]);
225       }
226 
227     Statement newStmt = new Statement(newTarget, stmt.getMethodName(), newArgs);
228 
229     try
230       {
231         newStmt.execute();
232       }
233     catch (Exception e)
234       {
235         getExceptionListener().exceptionThrown(e);
236 
237         // In Statement.writeStatement we had no possibility to flags
238         // an erroneous state to the ScanEngine without behaving different
239         // to the JDK.
240         scanEngine.revoke();
241         return;
242       }
243 
244     scanEngine.end();
245   }
246 
writeObject(Object o)247   public void writeObject(Object o)
248   {
249     accessCounter++;
250 
251     scanEngine.writeObject(o);
252 
253     if (get(o) == null)
254       super.writeObject(o);
255 
256     accessCounter--;
257   }
258 
setOwner(Object o)259   public void setOwner(Object o)
260   {
261     owner = o;
262   }
263 
getOwner()264   public Object getOwner()
265   {
266     return owner;
267   }
268 
269 }
270