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
039package java.beans;
040
041import gnu.java.beans.encoder.ScanEngine;
042
043import 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 */
053public 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}