001    package railo.commons.lang;
002    
003    import java.io.ByteArrayOutputStream;
004    import java.io.ObjectOutputStream;
005    import java.lang.reflect.Method;
006    import java.util.HashSet;
007    import java.util.Iterator;
008    import java.util.List;
009    import java.util.Map;
010    import java.util.Set;
011    
012    import railo.loader.engine.CFMLEngineFactory;
013    import railo.loader.util.Util;
014    import railo.runtime.exp.PageException;
015    import railo.runtime.type.Collection;
016    import railo.runtime.type.Query;
017    import railo.runtime.util.Creation;
018    
019    public class SizeAndCount {
020    
021            private static final int OBJECT_GRANULARITY_IN_BYTES = 8;
022            private static final int WORD_SIZE = Arch.getVMArchitecture().getWordSize();
023            private static final int HEADER_SIZE = 2 * WORD_SIZE;
024    
025        private static final int DOUBLE_SIZE = 8;
026        private static final int FLOAT_SIZE = 4;
027        private static final int LONG_SIZE = 8;
028        private static final int INT_SIZE = 4;
029        private static final int SHORT_SIZE = 2;
030        private static final int BYTE_SIZE = 1;
031        private static final int BOOLEAN_SIZE = 1;
032        private static final int CHAR_SIZE = 2;
033        private static final int REF_SIZE = WORD_SIZE;
034            
035            public static Size sizeOf(Object obj) throws PageException {
036                    Creation creator = CFMLEngineFactory.getInstance().getCreationUtil();
037                    Size size = new Size(0,0);
038                    sizeOf(creator,size, obj, new HashSet<Object>());
039                    return size;
040            }
041            
042            private static void sizeOf(Creation creator,Size size,Object obj, Set<Object> parents) throws PageException {
043                    if(obj==null) return;
044                    Object raw=obj;
045                    
046                    // TODO this is just a patch solution, find a better way to handle this kind of situation (Wrapper classes)
047                    if(isInstaneOf(obj.getClass(),"railo.runtime.text.xml.struct.XMLStruct")) {
048                            try {
049                                    Method toNode = raw.getClass().getMethod("toNode", new Class[0]);
050                                    raw=toNode.invoke(obj, new Object[0]);
051                            } 
052                            catch (Throwable e) {
053                                    e.printStackTrace();
054                            }       
055                    }
056                    
057                    
058                    if(parents.contains(raw)) return;
059                    parents.add(raw);
060                    try {
061                            if(obj instanceof Collection) {
062                                    if(obj instanceof Query) 
063                                            sizeOf(creator,size,(Query)obj,parents);
064                                    else
065                                            sizeOf(creator,size,((Collection)obj).valueIterator(),parents);
066                                    return;
067                            }
068                            // Map
069                            else if(obj instanceof Map) {
070                                    sizeOf(creator,size,((Map)obj).values().iterator(),parents);
071                                    return;
072                            }
073                            // List
074                            else if(obj instanceof List) {
075                                    sizeOf(creator,size,((List)obj).iterator(),parents);
076                                    return;
077                            }
078                            
079                            // String
080                            else if(obj instanceof String){
081                            size.size+= (CHAR_SIZE*((String)obj).length())+REF_SIZE;
082                    }
083                    // Number
084                            else if(obj instanceof Number){
085                            if(obj instanceof Double) size.size+=  DOUBLE_SIZE+REF_SIZE;
086                            else if(obj instanceof Float) size.size+=  FLOAT_SIZE+REF_SIZE;
087                            else if(obj instanceof Long) size.size+=  LONG_SIZE+REF_SIZE;
088                            else if(obj instanceof Integer) size.size+=  INT_SIZE+REF_SIZE;
089                            else if(obj instanceof Short) size.size+=  SHORT_SIZE+REF_SIZE;
090                            else if(obj instanceof Byte) size.size+=  BYTE_SIZE+REF_SIZE;
091                    }
092    
093                            else if(obj instanceof Boolean) size.size+= REF_SIZE+BOOLEAN_SIZE;
094                            else if(obj instanceof Character) size.size+= REF_SIZE+CHAR_SIZE;
095                            
096                            else size.size+=_sizeOf(obj);
097                            
098                            size.count++;
099                    }
100                    finally {
101                            //parents.remove(raw);// TODO should we not remove, to see if sister is me.
102                    }
103            }
104    
105            private static void sizeOf(Creation creator,Size size,Iterator it, Set<Object> parents) throws PageException {
106                    size.count++;
107                    size.size+=REF_SIZE;
108                    while(it.hasNext()){
109                            sizeOf(creator,size,it.next(),parents);
110                    }
111            }
112            
113            private static void sizeOf(Creation creator,Size size,Query qry, Set<Object> parents) throws PageException {
114                    size.count++;
115                    size.size+=REF_SIZE;
116                    int rows=qry.getRecordcount();
117                    String[] strColumns = qry.getColumns();
118                    Collection.Key[] columns = new Collection.Key[strColumns.length];
119                    for(int col=0;col<columns.length;col++){
120                            columns[col]=creator.createKey(strColumns[col]);
121                    }
122                    
123                    
124                    for(int row=1;row<=rows;row++){
125                            for(int col=0;col<columns.length;col++){
126                                    sizeOf(creator,size,qry.getAt(columns[col], row),parents);
127                            }
128                    }
129            }
130            
131            public static boolean isInstaneOf(Class src ,String className) {
132                    if(src==null) return false;
133            if(className.equals(src.getName()))return true;
134            
135            // interfaces
136            Class[] interfaces = src.getInterfaces();
137            for(int i=0;i<interfaces.length;i++){
138                    if(isInstaneOf(interfaces[i], className)) return true;
139            }
140            return isInstaneOf(src.getSuperclass(), className);
141        }  
142            
143            public static int  _sizeOf(Object o) {
144                    //System.err.println(o.getClass().getName());
145                    ByteArrayOutputStream os = new ByteArrayOutputStream();
146                    ObjectOutputStream oos=null;
147            try {
148                    oos = new ObjectOutputStream(os);
149                    oos.writeObject(o);
150            }
151            catch(Throwable t){}
152            finally {
153               Util.closeEL(oos);
154            }
155            return os.toByteArray().length;
156        }
157    
158    
159            public static class Size {
160    
161                    public int count;
162                    public int size;
163    
164                    public Size(int count, int size) {
165                            this.count=count;
166                            this.size=size;
167                    }
168                    
169            }
170            
171    }
172    
173    
174    class Arch {
175    
176            private static final Arch ARCH_32_BITS=new Arch(32, 4);
177            private static final Arch ARCH_64_BITS=new Arch(64, 8);
178            private static final Arch ARCH_UNKNOWN=new Arch(32, 4);
179        
180        private int bits;
181        private int wordSize;
182    
183        private Arch(int bits, int wordSize) {
184            this.bits = bits;
185            this.wordSize = wordSize;
186        }
187    
188        public int getBits() {
189            return bits;
190        }
191    
192        public int getWordSize() {
193            return wordSize;
194        }
195    
196        public static Arch getVMArchitecture() {
197            String archString = System.getProperty("sun.arch.data.model");
198            if (archString != null) {
199                if (archString.equals("32")) {
200                    return ARCH_32_BITS;
201                } else if (archString.equals("64")) {
202                    return ARCH_64_BITS;
203                }
204            }
205            return ARCH_UNKNOWN;
206        }
207        
208    }