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}