001 /* XMLEncoder.java
002 Copyright (C) 2004, 2005 Free Software Foundation, Inc.
003
004 This file is part of GNU Classpath.
005
006 GNU Classpath is free software; you can redistribute it and/or modify
007 it under the terms of the GNU General Public License as published by
008 the Free Software Foundation; either version 2, or (at your option)
009 any later version.
010
011 GNU Classpath is distributed in the hope that it will be useful, but
012 WITHOUT ANY WARRANTY; without even the implied warranty of
013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 General Public License for more details.
015
016 You should have received a copy of the GNU General Public License
017 along with GNU Classpath; see the file COPYING. If not, write to the
018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019 02110-1301 USA.
020
021 Linking this library statically or dynamically with other modules is
022 making a combined work based on this library. Thus, the terms and
023 conditions of the GNU General Public License cover the whole
024 combination.
025
026 As a special exception, the copyright holders of this library give you
027 permission to link this library with independent modules to produce an
028 executable, regardless of the license terms of these independent
029 modules, and to copy and distribute the resulting executable under
030 terms of your choice, provided that you also meet, for each linked
031 independent module, the terms and conditions of the license of that
032 module. An independent module is a module which is not derived from
033 or based on this library. If you modify this library, you may extend
034 this exception to your version of the library, but you are not
035 obligated to do so. If you do not wish to do so, delete this
036 exception statement from your version. */
037
038
039 package java.beans;
040
041 import gnu.java.beans.encoder.ScanEngine;
042
043 import java.io.OutputStream;
044
045 /**
046 * This class uses the {@link PersistenceDelegate} and {@link Encoder}
047 * infrastructure to generate an XML representation of the objects it
048 * serializes.
049 *
050 * @author Robert Schuster (robertschuster@fsfe.org)
051 * @since 1.4
052 */
053 public class XMLEncoder extends Encoder
054 {
055 Object owner;
056
057 Exception exception;
058
059 ScanEngine scanEngine;
060
061 private int accessCounter = 0;
062
063 public XMLEncoder(OutputStream os)
064 {
065 scanEngine = new ScanEngine(os);
066 }
067
068 public void close()
069 {
070 if (scanEngine != null)
071 {
072 scanEngine.close();
073 scanEngine = null;
074 }
075 }
076
077 public void flush()
078 {
079 scanEngine.flush();
080 }
081
082 public void writeExpression(Expression expr)
083 {
084 // Implementation note: Why is this method overwritten and nearly exactly
085 // reimplemented as in Encoder?
086 // The Encoder class can (and should be) subclassed by users outside of the
087 // java.beans package. While I have doubts that this is possible from an
088 // API design point of view I tried to replicate the Encoder's behavior
089 // in the JDK as exactly as possible. This strictness however made it
090 // extremely complicated to implement the XMLEncoder's backend. Therefore
091 // I decided to copy the Encoder's implementation and make all changes
092 // I needed for a succesfull operation of XMLEncoder.
093 //
094 // The same is true for the writeStatement method.
095
096 // Silently ignore out of bounds calls.
097 if (accessCounter <= 0)
098 return;
099
100 scanEngine.writeExpression(expr);
101
102
103 Object target = expr.getTarget();
104 Object value = null;
105 Object newValue = null;
106
107 try
108 {
109 value = expr.getValue();
110 }
111 catch (Exception e)
112 {
113 getExceptionListener().exceptionThrown(e);
114 return;
115 }
116
117
118 newValue = get(value);
119
120 if (newValue == null)
121 {
122 Object newTarget = get(target);
123 if (newTarget == null)
124 {
125 writeObject(target);
126 newTarget = get(target);
127
128 // May happen if exception was thrown.
129 if (newTarget == null)
130 {
131 return;
132 }
133 }
134
135 Object[] args = expr.getArguments();
136 Object[] newArgs = new Object[args.length];
137
138 for (int i = 0; i < args.length; i++)
139 {
140 newArgs[i] = get(args[i]);
141 if (newArgs[i] == null || isImmutableType(args[i].getClass()))
142 {
143 writeObject(args[i]);
144 newArgs[i] = get(args[i]);
145 }
146 }
147
148 Expression newExpr = new Expression(newTarget, expr.getMethodName(),
149 newArgs);
150
151 // Fakes the result of Class.forName(<primitiveType>) to make it possible
152 // to hand such a type to the encoding process.
153 if (value instanceof Class && ((Class) value).isPrimitive())
154 newExpr.setValue(value);
155
156 // Instantiates the new object.
157 try
158 {
159 newValue = newExpr.getValue();
160
161 putCandidate(value, newValue);
162 }
163 catch (Exception e)
164 {
165 getExceptionListener().exceptionThrown(e);
166
167 // In Statement.writeExpression we had no possibility to flags
168 // an erroneous state to the ScanEngine without behaving different
169 // to the JDK.
170 scanEngine.revoke();
171
172 return;
173 }
174
175 writeObject(value);
176
177 }
178 else if(value.getClass() == String.class || value.getClass() == Class.class)
179 {
180 writeObject(value);
181 }
182
183 scanEngine.end();
184 }
185
186 public void writeStatement(Statement stmt)
187 {
188 // In case of questions have a at the implementation note in
189 // writeExpression.
190
191 scanEngine.writeStatement(stmt);
192
193 // Silently ignore out of bounds calls.
194 if (accessCounter <= 0)
195 return;
196
197 Object target = stmt.getTarget();
198
199 Object newTarget = get(target);
200 if (newTarget == null)
201 {
202 writeObject(target);
203 newTarget = get(target);
204 }
205
206 Object[] args = stmt.getArguments();
207 Object[] newArgs = new Object[args.length];
208
209 for (int i = 0; i < args.length; i++)
210 {
211 // Here is the difference to the original writeStatement
212 // method in Encoder. In case that the object is known or
213 // not an immutable we put it directly into the ScanEngine
214 // which will then generate an object reference for it.
215 newArgs[i] = get(args[i]);
216 if (newArgs[i] == null || isImmutableType(args[i].getClass()))
217 {
218 writeObject(args[i]);
219 newArgs[i] = get(args[i]);
220 }
221 else
222 scanEngine.writeObject(args[i]);
223 }
224
225 Statement newStmt = new Statement(newTarget, stmt.getMethodName(), newArgs);
226
227 try
228 {
229 newStmt.execute();
230 }
231 catch (Exception e)
232 {
233 getExceptionListener().exceptionThrown(e);
234
235 // In Statement.writeStatement we had no possibility to flags
236 // an erroneous state to the ScanEngine without behaving different
237 // to the JDK.
238 scanEngine.revoke();
239 return;
240 }
241
242 scanEngine.end();
243 }
244
245 public void writeObject(Object o)
246 {
247 accessCounter++;
248
249 scanEngine.writeObject(o);
250
251 if (get(o) == null)
252 super.writeObject(o);
253
254 accessCounter--;
255 }
256
257 public void setOwner(Object o)
258 {
259 owner = o;
260 }
261
262 public Object getOwner()
263 {
264 return owner;
265 }
266
267 }