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    }