001    /*
002     @license.text@
003      */
004    package biz.hammurapi.xml.dom;
005    
006    import java.lang.reflect.Field;
007    import java.lang.reflect.Method;
008    import java.lang.reflect.Modifier;
009    import java.util.HashMap;
010    import java.util.HashSet;
011    import java.util.Map;
012    import java.util.Set;
013    
014    import org.w3c.dom.Element;
015    
016    import biz.hammurapi.convert.Converter;
017    
018    /**
019     * @author Pavel Vlasov
020     *
021     * @version $Revision: 1.4 $
022     */
023    public class BeanDomSerializer {
024        private class StackEntry {
025            long counter;
026            
027            private class IdentityWrapper {
028                    Object o;
029                    
030                    public IdentityWrapper(Object o) {
031                            this.o=o;
032                            }
033                    
034                    public int hashCode() {
035                            return o.hashCode();
036                    }
037                    
038                    public boolean equals(Object obj) {
039                            return obj instanceof IdentityWrapper && o==((IdentityWrapper) obj).o;
040                    }
041            }
042            
043            Map identityMap=new HashMap();
044            Map referenceMap=new HashMap();
045            
046            Long getReference(Object o) {
047                if (o instanceof Number || o instanceof String) {
048                    return (Long) referenceMap.get(o);
049                }
050                
051                return (Long) identityMap.get(new IdentityWrapper(o));
052            }
053            
054            long addReference(Object o) {
055                if (o instanceof Number || o instanceof String) {
056                    Long ref=(Long) referenceMap.get(o);
057                    if (ref==null) {
058                        long next=counter++;
059                        referenceMap.put(o, new Long(next));
060                        return next;
061                    }
062                    
063                    return ref.longValue();
064                }
065                
066                IdentityWrapper iw = new IdentityWrapper(o);
067                
068                Long ref=(Long) identityMap.get(iw);
069                if (ref==null) {
070                    long next=counter++;
071                    identityMap.put(iw, new Long(next));
072                    return next;
073                }
074                
075                return ref.longValue();
076            }
077        }
078        
079            private static ThreadLocal stack=new ThreadLocal();
080    
081            private Set badMethods=new HashSet();
082    
083            public DomSerializable convert(final Object object, final Converter master) {
084                    if (object!=null) {
085                            return new DomSerializable() {
086    
087                                    public void toDom(Element holder) {
088                                        if (object instanceof Class) {
089                                            holder.appendChild(holder.getOwnerDocument().createTextNode(((Class) object).getName()));
090                                            return;
091                                        }
092                                        
093                                        StackEntry sEntry=(StackEntry) stack.get();
094                                        boolean clean = sEntry==null;
095                                        if (clean) {
096                                            sEntry=new StackEntry();
097                                            stack.set(sEntry);
098                                        }
099                                        
100                                        try {
101                                            
102                                            Long refId=sEntry.getReference(object);
103                                            if (refId!=null) {
104                                                    holder.setAttribute("refid", refId.toString());
105                                                    return;
106                                            } 
107                                            
108                                                long nextCounter = sEntry.addReference(object);
109                                                //System.out.println("New id: "+nextCounter);
110                                                    holder.setAttribute("id", String.valueOf(nextCounter));
111                                                
112                                                holder.setAttribute("type", object.getClass().getName());
113                                                holder.appendChild(holder.getOwnerDocument().createTextNode(object.toString()));
114                                                
115                                                if (master!=null) {
116                                                        Class beanClass=object.getClass();
117                                                            final Object[] args = new Object[] {};
118                                                        Method[] methods = beanClass.getMethods();
119                                                        for (int i=0; i<methods.length; i++) {
120                                                            // getXXX() methods. Object.getClass() is not included.
121                                                            Method method = methods[i];
122                                                                    if (!(void.class.equals(method.getReturnType()))
123                                                                                    && Modifier.isPublic(method.getModifiers())
124                                                                            && !Modifier.isAbstract(method.getModifiers())
125                                                                            && !(method.getDeclaringClass().equals(Object.class)) 
126                                                                            && !Modifier.isStatic(method.getModifiers()) 
127                                                                            && method.getName().startsWith("get") 
128                                                                            && method.getParameterTypes().length==0
129                                                                            && !isBad(method)) {
130                                                                    try {
131                                                                                    Object value=method.invoke(object, args);
132                                                                                    if (value!=object) {
133                                                                                            DomSerializable vds=(DomSerializable) master.convert(value, DomSerializable.class, null);
134                                                                                            if (vds!=null) {
135                                                                                            Element ve=holder.getOwnerDocument().createElement(method.getName().substring(3));                      
136                                                                                            holder.appendChild(ve);
137                                                                                            vds.toDom(ve);
138                                                                                            }
139                                                                                    }
140                                                                            } catch (Exception e) {
141                                                                                    synchronized (badMethods) {
142                                                                                            badMethods.add(method);
143                                                                                    }
144                                                                            handleAccessError(holder, method, e, master);
145                                                                            }
146                                                            }                                                                                               
147                                                        }
148                                                        
149                                                        Field[] fields = beanClass.getFields();
150                                                        for (int i=0; i<fields.length; i++) {
151                                                            Field field = fields[i];
152                                                                    if (!Modifier.isStatic(field.getModifiers()) && Modifier.isPublic(field.getModifiers())) {
153                                                                    try {
154                                                                                    Object value=field.get(object);                                                         
155                                                                                    DomSerializable vds=(DomSerializable) master.convert(value, DomSerializable.class, null);
156                                                                                    if (vds!=null) {
157                                                                                    Element ve=holder.getOwnerDocument().createElement(field.getName());                    
158                                                                                    holder.appendChild(ve);
159                                                                                    vds.toDom(ve);
160                                                                                    }
161                                                                            } catch (Exception e) {
162                                                                            handleAccessError(holder, field, e, master);
163                                                                            }
164                                                            }
165                                                        }
166                                                } 
167                                        } finally {
168                                            if (clean) {
169                                                stack.set(null);
170                                            }
171                                        }
172                                    }
173    
174                                    private boolean isBad(Method method) {
175                                            synchronized (badMethods) {
176                                                    return badMethods.contains(method);
177                                            }
178                                    }                               
179                            };
180                    }
181                    
182                    return null;
183            }
184    
185            private void handleAccessError(Element holder, java.lang.reflect.Member member, Throwable e, Converter master) {
186                    Element ee=holder.getOwnerDocument().createElement("access-error");
187                    holder.appendChild(ee);
188                    ee.setAttribute("member", member.toString());
189                    DomSerializable eds=(DomSerializable) master.convert(e, DomSerializable.class, null);
190                    eds.toDom(ee);
191            }
192    }