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 054 extends Encoder 055 implements AutoCloseable 056{ 057 Object owner; 058 059 Exception exception; 060 061 ScanEngine scanEngine; 062 063 private int accessCounter = 0; 064 065 public XMLEncoder(OutputStream os) 066 { 067 scanEngine = new ScanEngine(os); 068 } 069 070 public void close() 071 { 072 if (scanEngine != null) 073 { 074 scanEngine.close(); 075 scanEngine = null; 076 } 077 } 078 079 public void flush() 080 { 081 scanEngine.flush(); 082 } 083 084 public void writeExpression(Expression expr) 085 { 086 // Implementation note: Why is this method overwritten and nearly exactly 087 // reimplemented as in Encoder? 088 // The Encoder class can (and should be) subclassed by users outside of the 089 // java.beans package. While I have doubts that this is possible from an 090 // API design point of view I tried to replicate the Encoder's behavior 091 // in the JDK as exactly as possible. This strictness however made it 092 // extremely complicated to implement the XMLEncoder's backend. Therefore 093 // I decided to copy the Encoder's implementation and make all changes 094 // I needed for a succesfull operation of XMLEncoder. 095 // 096 // The same is true for the writeStatement method. 097 098 // Silently ignore out of bounds calls. 099 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 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 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 259 public void setOwner(Object o) 260 { 261 owner = o; 262 } 263 264 public Object getOwner() 265 { 266 return owner; 267 } 268 269}