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    }