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.xml.dom.CompositeDomSerializer.Member;
017    
018    /**
019     * @author Pavel Vlasov
020     *
021     * @version $Revision: 1.4 $
022     */
023    public class BeanDomSerializer implements Member {
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 CompositeDomSerializer owner;   
082            
083            private Set badMethods=new HashSet();
084    
085            public DomSerializable toDomSerializable(final Object object) {
086                    if (object!=null) {
087                            return new DomSerializable() {
088    
089                                    public void toDom(Element holder) {
090                                        if (object instanceof Class) {
091                                            holder.appendChild(holder.getOwnerDocument().createTextNode(((Class) object).getName()));
092                                            return;
093                                        }
094                                        
095                                        StackEntry sEntry=(StackEntry) stack.get();
096                                        boolean clean = sEntry==null;
097                                        if (clean) {
098                                            sEntry=new StackEntry();
099                                            stack.set(sEntry);
100                                        }
101                                        
102                                        try {
103                                            
104                                            Long refId=sEntry.getReference(object);
105                                            if (refId!=null) {
106                                                    holder.setAttribute("refid", refId.toString());
107                                                    return;
108                                            } 
109                                            
110                                                long nextCounter = sEntry.addReference(object);
111                                                //System.out.println("New id: "+nextCounter);
112                                                    holder.setAttribute("id", String.valueOf(nextCounter));
113                                                
114                                                holder.setAttribute("type", object.getClass().getName());
115                                                holder.appendChild(holder.getOwnerDocument().createTextNode(object.toString()));
116                                                
117                                                if (owner!=null) {
118                                                        Class beanClass=object.getClass();
119                                                            final Object[] args = new Object[] {};
120                                                        Method[] methods = beanClass.getMethods();
121                                                        for (int i=0; i<methods.length; i++) {
122                                                            // getXXX() methods. Object.getClass() is not included.
123                                                            Method method = methods[i];
124                                                                    if (!(void.class.equals(method.getReturnType()))
125                                                                                    && Modifier.isPublic(method.getModifiers())
126                                                                            && !Modifier.isAbstract(method.getModifiers())
127                                                                            && !(method.getDeclaringClass().equals(Object.class)) 
128                                                                            && !Modifier.isStatic(method.getModifiers()) 
129                                                                            && method.getName().startsWith("get") 
130                                                                            && method.getParameterTypes().length==0
131                                                                            && !isBad(method)) {
132                                                                    try {
133                                                                                    Object value=method.invoke(object, args);
134                                                                                    if (value!=object) {
135                                                                                            DomSerializable vds=owner.getStackHead().toDomSerializable(value);
136                                                                                            if (vds!=null) {
137                                                                                            Element ve=holder.getOwnerDocument().createElement(method.getName().substring(3));                      
138                                                                                            holder.appendChild(ve);
139                                                                                            vds.toDom(ve);
140                                                                                            }
141                                                                                    }
142                                                                            } catch (Exception e) {
143                                                                                    synchronized (badMethods) {
144                                                                                            badMethods.add(method);
145                                                                                    }
146                                                                            handleAccessError(holder, method, e);
147                                                                            }
148                                                            }                                                                                               
149                                                        }
150                                                        
151                                                        Field[] fields = beanClass.getFields();
152                                                        for (int i=0; i<fields.length; i++) {
153                                                            Field field = fields[i];
154                                                                    if (!Modifier.isStatic(field.getModifiers()) && Modifier.isPublic(field.getModifiers())) {
155                                                                    try {
156                                                                                    Object value=field.get(object);                                                         
157                                                                                    DomSerializable vds=owner.getStackHead().toDomSerializable(value);
158                                                                                    if (vds!=null) {
159                                                                                    Element ve=holder.getOwnerDocument().createElement(field.getName());                    
160                                                                                    holder.appendChild(ve);
161                                                                                    vds.toDom(ve);
162                                                                                    }
163                                                                            } catch (Exception e) {
164                                                                            handleAccessError(holder, field, e);
165                                                                            }
166                                                            }
167                                                        }
168                                                } 
169                                        } finally {
170                                            if (clean) {
171                                                stack.set(null);
172                                            }
173                                        }
174                                    }
175    
176                                    private boolean isBad(Method method) {
177                                            synchronized (badMethods) {
178                                                    return badMethods.contains(method);
179                                            }
180                                    }                               
181                            };
182                    }
183                    
184                    return null;
185            }
186    
187            /**
188             * Callback method
189             */
190            public void setOwner(CompositeDomSerializer owner) {
191                    this.owner=owner;
192            }
193    
194            private void handleAccessError(Element holder, java.lang.reflect.Member member, Throwable e) {
195                    Element ee=holder.getOwnerDocument().createElement("access-error");
196                    holder.appendChild(ee);
197                    ee.setAttribute("member", member.toString());
198                    DomSerializable eds=owner.getStackHead().toDomSerializable(e);
199                    eds.toDom(ee);
200            }
201    }