001 /* 002 Copyright (C) 2003 Laurent Martelli <laurent@aopsys.com> 003 004 This program is free software; you can redistribute it and/or modify 005 it under the terms of the GNU Lesser General Public License as 006 published by the Free Software Foundation; either version 2 of the 007 License, or (at your option) any later version. 008 009 This program is distributed in the hope that it will be useful, 010 but WITHOUT ANY WARRANTY; without even the implied warranty of 011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 012 GNU Lesser General Public License for more details. 013 014 You should have received a copy of the GNU Lesser General Public License 015 along with this program; if not, write to the Free Software 016 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 017 018 package org.objectweb.jac.aspects.export; 019 020 import java.io.File; 021 import java.io.IOException; 022 import java.io.InputStream; 023 import java.io.Reader; 024 import java.util.HashSet; 025 import java.util.Hashtable; 026 import java.util.LinkedList; 027 import java.util.List; 028 import java.util.Map; 029 import java.util.Set; 030 import java.util.Vector; 031 import org.apache.log4j.Logger; 032 import org.apache.xerces.parsers.SAXParser; 033 import org.objectweb.jac.aspects.integrity.IntegrityAC; 034 import org.objectweb.jac.aspects.persistence.ValueConverter; 035 import org.objectweb.jac.core.ACManager; 036 import org.objectweb.jac.core.NameRepository; 037 import org.objectweb.jac.core.Naming; 038 import org.objectweb.jac.core.rtti.ClassItem; 039 import org.objectweb.jac.core.rtti.ClassRepository; 040 import org.objectweb.jac.core.rtti.CollectionItem; 041 import org.objectweb.jac.core.rtti.FieldItem; 042 import org.objectweb.jac.util.Files; 043 import org.objectweb.jac.util.Strings; 044 import org.xml.sax.Attributes; 045 import org.xml.sax.ContentHandler; 046 import org.xml.sax.InputSource; 047 import org.xml.sax.SAXException; 048 import org.xml.sax.XMLReader; 049 import org.xml.sax.helpers.DefaultHandler; 050 051 /** 052 * @see Exporter 053 */ 054 public class Importer extends DefaultHandler implements ContentHandler { 055 static Logger logger = Logger.getLogger("import"); 056 057 NameRepository nr; 058 ClassRepository cr; 059 XMLReader xmlReader; 060 061 boolean firstPass = true; 062 063 public Importer() { 064 nr = (NameRepository)NameRepository.get(); 065 cr = ClassRepository.get(); 066 xmlReader = new SAXParser(); 067 xmlReader.setContentHandler(this); 068 } 069 070 Hashtable counters = new Hashtable(); 071 072 /** 073 * Imports objects from a file created by the exporter, using 074 * UTF-8 encoding. All fields must have proper setters, or their 075 * values won't be properly restored regarding a persistence 076 * aspect. 077 * 078 * @param file the file to read the objects data from. It can be compressed. 079 * @see #importObjects(File,String) 080 */ 081 public void importObjects(File file) throws IOException, SAXException 082 { 083 importObjects(file,ExportAC.DEFAULT_ENCODING); 084 } 085 086 /** 087 * Imports objects from a file created by the exporter. All fields 088 * must have proper setters, or their values won't be properly 089 * restored regarding a persistence aspect. 090 * 091 * @param file the file to read the objects data from. It can be compressed. 092 * @param encoding charset encoding of the file 093 * 094 * @see #importObjects(File) 095 */ 096 public void importObjects(File file, String encoding) throws IOException, SAXException { 097 // First pass: create all objects 098 try { 099 firstPass = true; 100 Reader reader = Files.autoDecompressReader(file, encoding); 101 IntegrityAC.disableRoleUpdates(); 102 try { 103 xmlReader.parse(new InputSource(reader)); 104 ACManager.getACM().updateNameCounters(counters); 105 } finally { 106 reader.close(); 107 } 108 109 // Second pass: initialize field of objects created during first pass 110 firstPass = false; 111 reader = Files.autoDecompressReader(file, encoding); 112 try { 113 xmlReader.parse(new InputSource(reader)); 114 } finally { 115 reader.close(); 116 } 117 } finally { 118 IntegrityAC.enableRoleUpdates(); 119 newObjects.clear(); 120 } 121 } 122 123 public void startDocument() { 124 if (firstPass) 125 logger.info("FIRST PASS"); 126 else 127 logger.info("SECOND PASS"); 128 } 129 130 public void endDocument() { 131 if (firstPass) 132 logger.info("FIRST PASS DONE."); 133 else 134 logger.info("SECOND PASS DONE"); 135 } 136 137 // Current object 138 Object current = null; 139 // Name of current object 140 String currentName; 141 // Class of current object 142 ClassItem cl = null; 143 FieldItem field = null; 144 CollectionItem collection = null; 145 boolean reference = false; 146 boolean key = false; 147 boolean value = false; 148 boolean nameCounter = false; 149 // Map.Entry key 150 Object keyObject = null; 151 // Map.Entry value 152 Object valueObject = null; 153 // Name of counter 154 String name = null; 155 // value of counter 156 Long counter = null; 157 158 Set currentSet = new HashSet(); 159 Map currentMap = new Hashtable(); 160 List currentList = new Vector(); 161 162 // We must keep a reference on new objects so that they are not 163 // collected by the GC. 164 List newObjects = new LinkedList(); 165 166 public void startElement(String uri, String localName, String qName, Attributes attributes) { 167 if (localName.equals("object")) { 168 String name = attributes.getValue("name"); 169 currentName = name; 170 cl = cr.getClass(attributes.getValue("class")); 171 logger.info("<object name=\""+name+"\" class=\""+cl.getName()+"\">"); 172 current = nr.getObject(name); 173 if (current==null) { 174 if (firstPass) { 175 logger.debug(" No such object "+name); 176 try { 177 Naming.setName(name); 178 logger.info(" instanciating "+cl.getName()); 179 current = cl.newInstance(); 180 if (!nr.getName(current).equals(name)) 181 logger.error("Instanciated object is named "+ 182 nr.getName(current)+" instead of "+name); 183 newObjects.add(current); 184 } catch (Exception e) { 185 logger.error("Instanciation of "+cl.getName()+" failed:"+e); 186 } 187 } else { 188 logger.error("Object "+name+" was not instanciated during first pass?"); 189 } 190 } 191 } else if (localName.equals("field")) { 192 if (!firstPass) { 193 String name = attributes.getValue("name"); 194 logger.debug(" <field name=\""+name+"\">"); 195 if (current==null || cl==null) { 196 logger.error("No current object or class for <field class=\""+name+"\"> element"); 197 } else { 198 field = cl.getField(name); 199 if (field instanceof CollectionItem) { 200 collection = (CollectionItem)field; 201 //((CollectionItem)field).clear(current); 202 } 203 } 204 } 205 } else if (localName.equals("reference")) { 206 if (!firstPass) { 207 logger.debug(" <reference>"); 208 if (field!=null) { 209 reference = true; 210 buf.setLength(0); 211 } 212 } 213 } else if (localName.equals("primitive_value")) { 214 if (!firstPass) { 215 logger.debug(" <primitive_value>"); 216 if (field!=null) { 217 buf.setLength(0); 218 } 219 } 220 } else if (localName.equals("list")) { 221 if (!firstPass) { 222 if (collection!=null) { 223 currentList.clear(); 224 } else { 225 logger.error("No current collection field for <list>"); 226 } 227 } 228 } else if (localName.equals("set")) { 229 if (!firstPass) { 230 if (collection!=null) { 231 currentSet.clear(); 232 } else { 233 logger.error("No current collection field for <set>"); 234 } 235 } 236 } else if (localName.equals("map")) { 237 if (!firstPass) { 238 if (collection!=null) { 239 currentMap.clear(); 240 } else { 241 logger.error("No current collection field for <map>"); 242 } 243 } 244 } else if (localName.equals("entry")) { 245 } else if (localName.equals("value")) { 246 value = true; 247 } else if (localName.equals("key")) { 248 key = true; 249 } else if (localName.equals("nameCounter")) { 250 nameCounter = true; 251 } else if (localName.equals("name")) { 252 if (firstPass) { 253 logger.debug(" <name>"); 254 buf.setLength(0); 255 } 256 } else if (localName.equals("counter")) { 257 if (firstPass) { 258 logger.debug(" <counter>"); 259 buf.setLength(0); 260 } 261 } 262 } 263 264 public void endElement(String uri, String localName, String qName) { 265 try { 266 if (localName.equals("object")) { 267 current = null; 268 currentName = null; 269 cl = null; 270 } else if (localName.equals("field")) { 271 field = null; 272 collection = null; 273 } else if (localName.equals("reference")) { 274 if (!firstPass) { 275 String name = Strings.unslashify(buf.toString()); 276 Object ref = name.equals("null") ? null : nr.getObject(name); 277 if (key) { 278 keyObject = ref; 279 } else if (value) { 280 valueObject = ref; 281 } else if (field!=null) { 282 if (collection!=null) { 283 if (collection.isSet()) 284 currentSet.add(ref); 285 else 286 currentList.add(ref); 287 } else { 288 if (field.getThroughAccessor(current)!=ref) { 289 logger.info("Updating "+currentName+"."+field.getName()); 290 field.setThroughWriter(current,ref); 291 } 292 } 293 } 294 reference = false; 295 } 296 } else if (localName.equals("primitive_value")) { 297 if (!firstPass) { 298 Object val = 299 ValueConverter.stringToObject( 300 null,Strings.unslashify(buf.toString())); 301 if (key) { 302 keyObject = val; 303 } else if (value) { 304 valueObject = val; 305 } else if (field!=null) { 306 if (collection!=null) { 307 if (collection.isSet()) 308 currentSet.add(val); 309 else 310 currentList.add(val); 311 } else { 312 Object currentValue = field.getThroughAccessor(current); 313 if ((currentValue==null && val!=null) || 314 (currentValue!=null && !currentValue.equals(val))) { 315 logger.info("Updating "+currentName+"."+field.getName()); 316 field.setThroughWriter(current,val); 317 } 318 } 319 } 320 } 321 } else if (localName.equals("list")) { 322 if (!firstPass) { 323 List list = (List)collection.getThroughAccessor(current); 324 if (!currentList.equals(list)) { 325 logger.info("Updating list "+currentName+"."+field.getName()); 326 list.clear(); 327 list.addAll(currentList); 328 } else { 329 logger.debug(" List has not changed"); 330 } 331 currentList.clear(); 332 } 333 } else if (localName.equals("set")) { 334 if (!firstPass) { 335 Set set = (Set)collection.getThroughAccessor(current); 336 if (!currentSet.equals(set)) { 337 logger.info("Updating set "+currentName+"."+field.getName()); 338 set.clear(); 339 set.addAll(currentSet); 340 } else { 341 logger.debug(" Set has not changed"); 342 } 343 currentSet.clear(); 344 } 345 } else if (localName.equals("map")) { 346 if (!firstPass) { 347 Map map = (Map)collection.getThroughAccessor(current); 348 if (!currentMap.equals(map)) { 349 logger.info("Updating map "+currentName+"."+field.getName()); 350 map.clear(); 351 map.putAll(currentMap); 352 } else { 353 logger.debug(" Map has not changed"); 354 } 355 currentMap.clear(); 356 } 357 } else if (localName.equals("entry")) { 358 if (!firstPass) { 359 if (collection!=null) { 360 if (collection.isMap()) 361 currentMap.put(keyObject,valueObject); 362 else 363 logger.error("Field is not a Map: "+field); 364 } else { 365 logger.error("Field is not a collection: "+field); 366 } 367 } 368 } else if (localName.equals("value")) { 369 value = false; 370 } else if (localName.equals("key")) { 371 key = false; 372 } else if (localName.equals("nameCounter")) { 373 if (firstPass) { 374 counters.put(name,counter); 375 logger.info("Namecounter "+name+" -> "+counter); 376 nameCounter = false; 377 } 378 } else if (localName.equals("name")) { 379 if (firstPass) { 380 name = buf.toString(); 381 } 382 } else if (localName.equals("counter")) { 383 if (firstPass) { 384 counter = new Long(buf.toString()); 385 } 386 } 387 } catch (Exception e) { 388 logger.error("endElement("+uri+", "+localName+", "+qName+") failed"); 389 logger.error(" class="+cl); 390 logger.error(" field="+field); 391 logger.error(" current="+current); 392 logger.error(" buf="+buf,e); 393 } 394 } 395 396 StringBuffer buf = new StringBuffer(); 397 public void characters(char chars[], int start, int length) { 398 if (buf!=null) { 399 buf.append(chars,start,length); 400 } else { 401 //throw new RuntimeException("Unexpected characters: "+new String(chars)); 402 } 403 } 404 405 public void ignorableWhitespace(char chars[], int start, int length) { 406 characters(chars,start,length); 407 } 408 409 protected Object readValue(String s) throws IOException { 410 int colon = s.indexOf(':'); 411 if (colon==-1) { 412 return nr.getObject(s); 413 } else { 414 return ValueConverter.stringToObject(null,s); 415 } 416 } 417 }