001    /*
002      Copyright (C) 2003-2004 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 gnu.regexp.RE;
021    import java.io.FileOutputStream;
022    import java.io.IOException;
023    import java.io.OutputStream;
024    import java.io.OutputStreamWriter;
025    import java.io.Writer;
026    import java.lang.StringBuffer;
027    import java.util.Collection;
028    import java.util.HashMap;
029    import java.util.HashSet;
030    import java.util.Iterator;
031    import java.util.Map;
032    import java.util.Set;
033    import org.apache.log4j.Logger;
034    import org.objectweb.jac.aspects.naming.NamingAC;
035    import org.objectweb.jac.aspects.persistence.ValueConverter;
036    import org.objectweb.jac.core.ACManager;
037    import org.objectweb.jac.core.NameRepository;
038    import org.objectweb.jac.core.Wrappee;
039    import org.objectweb.jac.core.rtti.ClassItem;
040    import org.objectweb.jac.core.rtti.ClassRepository;
041    import org.objectweb.jac.core.rtti.CollectionItem;
042    import org.objectweb.jac.core.rtti.FieldItem;
043    import org.objectweb.jac.util.Strings;
044    import java.io.File;
045    
046    public class Exporter {
047        static Logger logger = Logger.getLogger("export");
048    
049        Set roots = new HashSet();
050        Set allow = new HashSet();
051        Set deny = new HashSet();
052    
053        Set allowRegexps = new HashSet();
054        Set denyRegexps = new HashSet();
055    
056        Map toExport = new HashMap(); // Objects not exported yet (OPath -> Object)
057        Set exported = new HashSet(); // Already exported objects
058    
059    
060        NameRepository nr;
061        public Exporter(Set roots, Set allow, Set deny) {
062            nr = (NameRepository)NameRepository.get();
063            this.roots = roots;
064            this.allow = allow;
065            Iterator i = allow.iterator();
066            while (i.hasNext()) {
067                String s = (String)i.next();
068                try {
069                    RE regexp = new RE(s);
070                    allowRegexps.add(regexp);
071                } catch (Exception e) {
072                    e.printStackTrace();
073                }
074            }
075            this.deny = deny;
076            i = deny.iterator();
077            while (i.hasNext()) {
078                String s = (String)i.next();
079                try {
080                    RE regexp = new RE(s);
081                    denyRegexps.add(regexp);
082                } catch (Exception e) {
083                    e.printStackTrace();
084                }
085            }
086        }
087    
088        /**
089         * Exports all objects to a file
090         */
091        public void export(File file) throws IOException {
092            FileOutputStream out = new FileOutputStream(file);
093            try {
094                export(out);
095            } finally {
096                out.close();
097            }
098        }
099    
100        public void export(OutputStream outStream) throws IOException {
101            export(outStream,"UTF-8");
102        }
103    
104        /**
105         * Exports all objects to a stream
106         */
107        public void export(OutputStream outStream, String encoding) throws IOException {
108            Writer out = new OutputStreamWriter(outStream,encoding);
109            out.write("<?xml version=\"1.0\" encoding=\""+encoding+"\"?>\n");
110            out.write("<export>\n");
111            int count = 0;
112            Iterator i = NameRepository.getObjects(roots).iterator();
113            while (i.hasNext()) {
114                Object o = i.next();
115                String name = nr.getName(o);
116                export(out,o,name,name);
117                count++;
118            }
119            while (!toExport.isEmpty()) {
120                i = new HashMap(toExport).entrySet().iterator();
121                while (i.hasNext()) {
122                    Map.Entry entry = (Map.Entry)i.next();
123                    Object o = entry.getKey();
124                    String opath = (String)entry.getValue();
125                    String name = nr.getName(o);
126                    export(out,o,name,opath);
127                    count++;
128                }
129            }
130            logger.info(count+" objects exported");
131            Map counters = 
132                ((NamingAC)ACManager.getACM().getACFromFullName("naming")).getNameCounters();
133            i = counters.entrySet().iterator();
134            while(i.hasNext()) {
135                Map.Entry entry = (Map.Entry)i.next();
136                out.write("<nameCounter>\n");
137                out.write("<name>"+entry.getKey()+"</name>\n");
138                out.write("<counter>"+entry.getValue()+"</counter>\n");
139                out.write("</nameCounter>\n");
140            }
141            logger.info("Name counters exported");
142            out.write("</export>\n");
143            out.flush();
144        }
145    
146        static public String escapeChar(char c) {
147            return (c == '<') ? "<" :
148                (c == '>') ? ">" :
149                (c == '\'') ? "'" :
150                (c == '\"') ? """ :
151                (c == '&') ? "&" :
152                (c == ']') ? "]" : 
153                //(c == '\n') ? "
" :
154                //(c == '\r') ? "
" :
155                null;
156        }
157    
158        static public String escapeString(String s) {
159            StringBuffer buf = new StringBuffer(s.length()*2);
160            for (int i=0; i<s.length(); i++) {
161                char c = s.charAt(i);
162                String escaped = escapeChar(c);
163                if (escaped==null)
164                    buf.append(c);
165                else
166                    buf.append(escaped);
167            }
168            return buf.toString();
169        }
170    
171        /**
172         * Tells wether instances of a class should be exported or not
173         */
174        protected boolean allowExport(ClassItem cl) {
175            Iterator i = allowRegexps.iterator();
176            while(i.hasNext()) {
177                RE regexp = (RE)i.next();
178                if (cl.isSubClassOf(regexp))
179                    return true;
180            }
181            return false;
182        }
183    
184        /**
185         * Exports an object to a stream
186         * @param out the stream to which to export
187         * @param o the object to export
188         * @param name name of the object to export
189         */
190        public void export(Writer out, Object o, String name ,String opath) throws IOException {
191            ClassItem cl = ClassRepository.get().getClass(o);
192            if (name==null) {
193                logger.error("Skipping unamed object "+o+" "+cl.getName()+" at "+opath);
194                new Exception().printStackTrace();
195                toExport.remove(o);
196                return;
197            }
198            if (exported.contains(o)) {
199                logger.debug("Skipping already exported "+name+" "+cl.getName());
200                toExport.remove(o);
201                return;
202            }
203            if (!allowExport(cl)) {
204                logger.debug("Skipping not allowed "+name+" "+cl.getName());
205                toExport.remove(o);
206                return;
207            }
208            logger.debug("Exporting "+name+" "+cl.getName());
209            exported.add(o);
210            out.write("<object name=\""+name+"\" class=\""+cl.getName()+"\">\n");
211            Iterator i = cl.getAllFields().iterator();
212            while (i.hasNext()) {
213                FieldItem field = (FieldItem)i.next();
214                if (!field.isCalculated() && !field.isTransient() && 
215                    !field.isStatic() && !field.isTransient()) 
216                {
217                    String newPath = opath+"."+field.getName();
218                    Object value = field.getThroughAccessor(o);
219                    out.write("  <field name=\""+field.getName()+"\">\n");
220                    try {
221                        if (field instanceof CollectionItem) {
222                            CollectionItem collection = (CollectionItem)field;
223                            if (collection.isMap()) {
224                                out.write("    <map>\n");
225                                Iterator j = ((Map)collection.getThroughAccessor(o)).entrySet().iterator();
226                                while (j.hasNext()) {
227                                    Map.Entry entry = (Map.Entry)j.next();
228                                    out.write("      <entry><key>");
229                                    writeValue(out,entry.getKey(),newPath+"[key]");
230                                    out.write("</key><value>");
231                                    writeValue(out,entry.getValue(),newPath+"["+entry.getKey()+"]");
232                                    out.write("</value></entry>\n");
233                                }
234                                out.write("    </map>\n");
235                            } else {
236                                if (collection.isList())
237                                    out.write("    <list>\n");
238                                else if (collection.isSet())
239                                    out.write("    <set>\n");
240                                Iterator j = ((Collection)collection.getThroughAccessor(o)).iterator();
241                                int count = 0;
242                                while (j.hasNext()) {
243                                    Object item = j.next();
244                                    out.write("      ");
245                                    writeValue(out,item,newPath+"["+count+"]");
246                                    out.write("\n");
247                                    count++;
248                                }
249                                if (collection.isList())
250                                    out.write("    </list>\n");
251                                else if (collection.isSet())
252                                    out.write("    </set>\n");
253                            }
254                        } else {
255                            out.write("    ");
256                            writeValue(out,value,newPath);
257                            out.write("\n");
258                        }
259                    } catch (Exception e) {
260                        logger.error("Failed to export "+name+"."+field);
261                    } finally {
262                        out.write("  </field>\n");
263                    }
264                }
265            }
266            out.write("</object>\n");
267        }
268    
269        protected void writeValue(Writer out, Object value, String opath) throws IOException {
270            if (value instanceof Wrappee) {
271                if (value!=null) {
272                    if (!exported.contains(value)) {
273                        toExport.put(value,opath);
274                        logger.debug("toExport "+opath+" -> "+value+" "+nr.getName(value));
275                    }
276                    out.write("<reference>"+nr.getName(value)+"</reference>");
277                } else {
278                    out.write("<reference>null</reference>");
279                }
280            } else {
281                if (value!=null) {
282                    out.write("<primitive_value>"+
283                              escapeString(Strings.slashify(ValueConverter.objectToString(null,value)))+
284                              "</primitive_value>");
285                } else {
286                    out.write("<primitive_value>null</primitive_value>");
287                }
288            }
289        }
290    }