/* * � Copyright IBM Corp. 2010, 2014 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. See the License for the specific language governing * permissions and limitations under the License. */ package com.ibm.xsp.extlib.util.debug; import java.beans.BeanInfo; import java.beans.IntrospectionException; import java.beans.PropertyDescriptor; import java.io.File; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.TimeZone; import sun.security.action.GetBooleanAction; import com.ibm.commons.util.DateTime; import com.ibm.commons.util.QuickSort; import com.ibm.commons.util.StringUtil; import com.ibm.xsp.extlib.log.ExtlibCoreLogger; /** * Default Java Factory. */ public class JavaDumpFactory implements DumpAccessorFactory { private static JavaDumpFactory instance = new JavaDumpFactory(); public static JavaDumpFactory get() { return instance; } public JavaDumpFactory() { } public DumpAccessor find(DumpContext dumpContext, Object o) { if(o==null) { return new NullValue(dumpContext); } Class<?> c = o.getClass(); // Check for a simple value if( o instanceof String || o instanceof Boolean || o instanceof Character || o instanceof Number || o instanceof Locale || o instanceof TimeZone || o instanceof File || o instanceof Date || o instanceof Calendar) { return new PrimitiveValue(dumpContext,o); } // Check for an array/collection if( c.isArray() ) { return new JavaArray(dumpContext,o); } if( o instanceof java.util.Map ) { return new JavaMap(dumpContext,(java.util.Map)o); } if( o instanceof Collection) { return new JavaCollection(dumpContext,(Collection)o); } // Try a Java bean if(dumpContext.shouldUseBeanProperties(o)) { try { return createJavaBean(dumpContext,o,null); } catch(Exception ex) { } } // Regular Java class return createJavaObject(dumpContext,o,null); } public static String toPrimitiveString(Object o) { if(o==null) { return "<null>"; // $NON-NLS-1$ } if( o instanceof String || o instanceof Boolean || o instanceof Character) { return o.toString(); } if(o instanceof Number) { if(o instanceof Double) { double d = ((Number)o).doubleValue(); long l = ((Number)o).longValue(); if(d==(double)l) { return Long.toString(l); } } if(o instanceof Float) { float f = ((Number)o).floatValue(); long l = ((Number)o).longValue(); if(f==(float)l) { return Long.toString(l); } } return o.toString(); } if(o instanceof Date) { return DateTime.formatDateTime((Date)o,DateTime.LONG_DATETIME); } if(o instanceof Calendar) { return DateTime.formatDateTime(new Date(((Calendar)o).getTimeInMillis()),DateTime.LONG_DATETIME); } if(o instanceof TimeZone) { return ((TimeZone)o).getDisplayName(); } if(o instanceof Locale) { return ((Locale)o).getDisplayName(); } return o.toString(); } public static class NullValue extends DumpAccessor.Value { public NullValue(DumpContext dumpContext) { super(dumpContext); } @Override public String getTypeAsString() { return "null"; // $NON-NLS-1$ } @Override public Object getValue() { return null; } @Override public String getValueAsString() { return "<null>"; // $NON-NLS-1$ } } public static class PrimitiveValue extends DumpAccessor.Value { protected Object value; public PrimitiveValue(DumpContext dumpContext, Object value) { super(dumpContext); this.value = value; } @Override public String getTypeAsString() { String cName = value.getClass().getName(); return cName; } @Override public Object getValue() { return value; } @Override public String getValueAsString() { return toPrimitiveString(value); } } public static class ExceptionValue extends DumpAccessor.Value { protected Throwable value; public ExceptionValue(DumpContext dumpContext, Throwable value) { super(dumpContext); this.value = value; } @Override public String getTypeAsString() { String cName = value.getClass().getName(); return cName; } @Override public Object getValue() { return value; } @Override public String getValueAsString() { return value.toString(); } } public static abstract class AbstractMap extends DumpAccessor.Map { public AbstractMap(DumpContext dumpContext) { super(dumpContext); } @Override public String[] getCategories() { return null; } @Override public Iterator<Object> getPropertyKeys(String category) { ArrayList<Object> list = new ArrayList<Object>(); getAllPropertyKeys(category,list); (new QuickSort.JavaList(list)).sort(); return list.iterator(); } public abstract void getAllPropertyKeys(String category, List<Object> list); //public abstract Object getProperty(String key); } public static class JavaMap extends AbstractMap { protected java.util.Map instance; public JavaMap(DumpContext dumpContext, java.util.Map instance) { super(dumpContext); this.instance = instance; } @Override public String getStringLabel() { if(instance.isEmpty()) { return "<empty map>"; // $NLS-JavaDumpFactory.emptymap-1$ } return null; } @Override public String getTypeAsString() { return "Java Map: "+instance.getClass().getName(); // $NON-NLS-1$ } protected boolean accept(Object key) { return true; } @Override public void getAllPropertyKeys(String category, List<Object> list) { for(Object o: instance.keySet() ) { if(accept(o)) { list.add(o); } } } @Override public Object getProperty(Object key) { return instance.get(key); } } public static DumpAccessor createJavaBean(DumpContext dumpContext, Object instance, JavaBean.IFilter filter) { try { return new JavaBean(dumpContext,instance,filter); } catch(Throwable t) { return new ExceptionValue(dumpContext,t); } } public static class JavaBean extends AbstractMap { public static interface IFilter { public boolean accept(PropertyDescriptor desc); } protected Object instance; protected PropertyDescriptor[] desc; protected IFilter filter; public JavaBean(DumpContext dumpContext, Object instance, IFilter filter) throws IntrospectionException { super(dumpContext); this.instance = instance; this.filter = filter; BeanInfo bi = java.beans.Introspector.getBeanInfo(instance.getClass()); this.desc = bi.getPropertyDescriptors(); } public Object getInstance() { return instance; } @Override public String getStringLabel() { if(desc==null || desc.length==0) { return "<no public property>"; // $NLS-JavaDumpFactory.nopublicproperty-1$ } return null; } @Override public String getTypeAsString() { return "Java Bean: "+instance.getClass().getName(); // $NON-NLS-1$ } protected boolean accept(PropertyDescriptor desc) { String name = desc.getName(); if(name.equals("class")) { // $NON-NLS-1$ return false; } if(filter!=null) { if(!filter.accept(desc)) { return false; } } return true; } @Override public void getAllPropertyKeys(String category, List<Object> list) { for( int i=0; i<desc.length; i++ ) { if(!accept(desc[i])) { continue; } list.add(desc[i].getName()); } } @Override public Object getProperty(Object key) { return getBeanProperty(instance, desc, key); } } public static Object getBeanProperty(Object instance, PropertyDescriptor[] desc, Object key) { for( int i=0; i<desc.length; i++ ) { if(StringUtil.equals(desc[i].getName(),key)) { try { Method read = desc[i].getReadMethod(); if(read==null) { String msg = StringUtil.format("<error: No bean read method>"); // $NON-NLS-1$ if( ExtlibCoreLogger.CORE.isWarnEnabled() ){ ExtlibCoreLogger.CORE.warnp(JavaDumpFactory.class, "getBeanProperty", //$NON-NLS-1$ msg); } return msg; } else { return read.invoke(instance,(Object[])null); } } catch(Throwable e) { if(e instanceof InvocationTargetException) { if(e.getCause()!=null) { e = e.getCause(); } } String msg = StringUtil.format("<error: {0}>",e.getMessage()); // $NON-NLS-1$ if( ExtlibCoreLogger.CORE.isWarnEnabled() ){ ExtlibCoreLogger.CORE.warnp(JavaDumpFactory.class, "getBeanProperty", //$NON-NLS-1$ e, msg); } return msg; } } } return StringUtil.format("<unknown bean property: {0}>",key); // $NON-NLS-1$ } public static DumpAccessor createJavaObject(DumpContext dumpContext, Object instance, JavaObject.IFilter filter) { try { return new JavaObject(dumpContext, instance,filter); } catch(Throwable t) { return new ExceptionValue(dumpContext,t); } } public static class JavaObject extends AbstractMap { public static interface IFilter { public boolean accept(Field filed); } protected Object instance; protected Field[] fields; protected IFilter filter; public JavaObject(DumpContext dumpContext, Object instance, IFilter filter) { super(dumpContext); this.instance = instance; this.filter = filter; this.fields = instance.getClass().getFields(); } public Object getInstance() { return instance; } @Override public String getStringLabel() { if(fields==null || fields.length==0) { return "<no public field>"; // $NLS-JavaDumpFactory.nopublicfield-1$ } return null; } @Override public String getTypeAsString() { return "Java Object: "+instance.getClass().getName(); // $NON-NLS-1$ } protected boolean accept(Field field) { if((field.getModifiers()&Modifier.PUBLIC)==0) { return false; } if((field.getModifiers()&Modifier.STATIC)!=0) { return false; } if(filter!=null) { if(!filter.accept(field)) { return false; } } return true; } @Override public void getAllPropertyKeys(String category, List<Object> list) { for( int i=0; i<fields.length; i++ ) { if(!accept(fields[i])) { continue; } list.add(fields[i].getName()); } } @Override public Object getProperty(Object key) { for( int i=0; i<fields.length; i++ ) { if(StringUtil.equals(fields[i].getName(),key)) { try { return fields[i].get(instance); } catch(Exception e) { String msg = StringUtil.format("<error: {0}>",e.getMessage()); // $NON-NLS-1$ if( ExtlibCoreLogger.CORE.isWarnEnabled() ){ ExtlibCoreLogger.CORE.warnp(JavaDumpFactory.class, "getProperty", //$NON-NLS-1$ e, msg); } return msg; } } } String msg = StringUtil.format("<unknown field: {0}>",key); // $NON-NLS-1$ if( ExtlibCoreLogger.CORE.isWarnEnabled() ){ ExtlibCoreLogger.CORE.warnp(JavaDumpFactory.class, "getProperty", //$NON-NLS-1$ msg); } return msg; } } public static class JavaArray extends DumpAccessor.Array { protected Object instance; public JavaArray(DumpContext dumpContext, Object instance) { super(dumpContext); this.instance = instance; } @Override public String getTypeAsString() { return "Java Array: "+instance.getClass().getName(); // $NON-NLS-1$ } @Override public Iterator<Object> arrayIterator() { return new Iterator<Object>() { int current = 0; int length = java.lang.reflect.Array.getLength(instance); public boolean hasNext() { return current<length; } public Object next() { if( current<length ) { return java.lang.reflect.Array.get(instance, current++); } return null; } public void remove() {} }; } } public static class JavaCollection extends DumpAccessor.Array { protected Collection instance; public JavaCollection(DumpContext dumpContext, Collection instance) { super(dumpContext); this.instance = instance; } @Override public String getTypeAsString() { return "Java Collection: "+instance.getClass().getName(); // $NON-NLS-1$ } @Override public Iterator<Object> arrayIterator() { return ((Collection<Object>)instance).iterator(); } } }