blob: 61f76c02fdb8701ab78f22bddae32a4fa14dfa54 [file] [log] [blame]
/*=========================================================================
* (c)Copyright 2002-2011, GemStone Systems, Inc. All Rights Reserved.
* 1260 NW Waterhouse Ave., Suite 200, Beaverton, OR 97006
* All Rights Reserved.
* =======================================================================*/
package com.gemstone.gemfire.mgmt.DataBrowser.query.export;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import com.gemstone.gemfire.mgmt.DataBrowser.query.ColumnNotFoundException;
import com.gemstone.gemfire.mgmt.DataBrowser.query.ColumnValueNotAvailableException;
import com.gemstone.gemfire.mgmt.DataBrowser.query.IntrospectionException;
import com.gemstone.gemfire.mgmt.DataBrowser.query.IntrospectionRepository;
import com.gemstone.gemfire.mgmt.DataBrowser.query.IntrospectionResult;
import com.gemstone.gemfire.mgmt.DataBrowser.query.QueryResult;
import com.gemstone.gemfire.mgmt.DataBrowser.query.QueryUtil;
import com.gemstone.gemfire.mgmt.DataBrowser.query.ResultSet;
import com.gemstone.gemfire.mgmt.DataBrowser.query.internal.CollectionTypeResultImpl;
import com.gemstone.gemfire.mgmt.DataBrowser.utils.LogUtil;
/**
* This class is responsible to navigate through the object graph of the Query
* result and help to prepare an application specific model representation of
* the graph. This is achieved by making call-backs to the application specific
* handlers.
*
* @author Hrishi
**/
public class QueryResultExporter {
private QueryDataExportHandler dataHandler;
private int depthRequired;
private int depth;
public QueryResultExporter(QueryDataExportHandler dataHndlr, int depthReq) {
dataHandler = dataHndlr;
depthRequired = depthReq;
depth = -1;
}
public int getDepthRequired() {
return depthRequired;
}
public void resetDepth() {
this.depth = 0;
}
public void exportData(QueryResult result) throws ColumnNotFoundException,
ColumnValueNotAvailableException {
if (this.dataHandler == null) {
throw new IllegalStateException(
"dataHandler is required to export data. Please provide valid dataHandler");
}
this.dataHandler.handleStartDocument();
this.depth = -2;
// Dummy type created so as to use handleCollectionType API for main
// resultset as well.
IntrospectionResult type = new CollectionTypeResultImpl(result
.getQueryResult().getClass());
IntrospectionRepository.singleton().addIntrospectionResult(
result.getQueryResult().getClass(), type);
Class<?> typeClass = type.getJavaType();
handleCollectionType("QueryResult", typeClass, result);
this.dataHandler.handleEndDocument();
}
public void exportObject(String name, IntrospectionResult metaInfo, Object obj)
throws ColumnNotFoundException, ColumnValueNotAvailableException {
// Increment the depth by 1 and check against the required depth.
boolean shallow = isRequiredDepthReached(true);
int resultType = metaInfo.getResultType();
// If shallow = true and colClass is not primitive, then we will treat this
// as a primitive type.
// This will stop the subsequent navigation of the object tree.
if ((shallow) || (resultType == IntrospectionResult.PRIMITIVE_TYPE_RESULT)) {
Class<?> type = metaInfo.getColumnClass(0);
Object value = obj;
dataHandler.handlePrimitiveType(name, type, value);
} else if (resultType == IntrospectionResult.COMPOSITE_TYPE_RESULT) {
handleCompositeType(name, metaInfo, obj);
} else if (resultType == IntrospectionResult.COLLECTION_TYPE_RESULT) {
ResultSet temp = (ResultSet) metaInfo.getColumnValue(obj, 0);
Class<?> type = metaInfo.getJavaType();
handleCollectionType(name, type, temp);
temp.close();
} else if (resultType == IntrospectionResult.STRUCT_TYPE_RESULT) {
handleStructType(name, metaInfo, obj);
} else if (resultType == IntrospectionResult.MAP_TYPE_RESULT) {
ResultSet temp = (ResultSet) metaInfo.getColumnValue(obj, 0);
Class<?> type = metaInfo.getJavaType();
handleCollectionType(name, type, temp);
temp.close();
} else if (resultType == IntrospectionResult.PDX_TYPE_RESULT) {
handlePdxType(name, metaInfo, obj);
}
// Since we are complete, decrement the depth by one.
decrDepth();
}
private void handleCompositeType(String nm, IntrospectionResult metaInfo,
Object value) throws ColumnNotFoundException,
ColumnValueNotAvailableException {
// Increment the depth by 1 and check against the required depth.
boolean shallow = isRequiredDepthReached(true);
String name = (null != nm) ? nm : metaInfo.getJavaType().getSimpleName();
// Start of the Composite object.
this.dataHandler.handleStartCompositeType(name, metaInfo, value);
// This object is not null. For a null object, there is no special
// processing.
if (value != null) {
for (int k = 0; k < metaInfo.getColumnCount(); k++) {
int columnType = metaInfo.getColumnType(value, k);
String colName = metaInfo.getColumnName(k);
Object colValue = metaInfo.getColumnValue(value, k);
Class<?> colClass = metaInfo.getColumnClass(value, k);
// If shallow = true and colClass is not primitive, then we will treat
// this as a primitive type.
// This will stop the subsequent navigation of the object tree.
if ((shallow)
|| (columnType == IntrospectionResult.PRIMITIVE_TYPE_COLUMN)) {
this.dataHandler.handlePrimitiveType(colName, colClass, colValue);
} else if (columnType == IntrospectionResult.COMPOSITE_TYPE_COLUMN) {
IntrospectionResult meta = IntrospectionRepository.singleton()
.getIntrospectionResult(colClass);
handleCompositeType(colName, meta, colValue);
} else if (columnType == IntrospectionResult.COLLECTION_TYPE_COLUMN) {
ResultSet temp = (ResultSet) colValue;
handleCollectionType(colName, colClass, temp);
temp.close();
} else if (columnType == IntrospectionResult.MAP_TYPE_COLUMN) {
ResultSet temp = (ResultSet) colValue;
handleCollectionType(colName, colClass, temp);
temp.close();
} else if (columnType == IntrospectionResult.PDX_TYPE_COLUMN ||
columnType == IntrospectionResult.PDX_OBJECT_TYPE_COLUMN) {//eventually goes to introspectAndHandleElement if it's not a PdxInstance
IntrospectionResult colMetaInfo = null;
try {
colMetaInfo = IntrospectionRepository.singleton().introspectTypeByObject(colValue);
handlePdxType(name, colMetaInfo, colValue);
} catch (IntrospectionException e) {
colMetaInfo = null;
LogUtil.warning("Could not introspect: "+colValue+", skipping it..", e);
}
}
}
}
this.dataHandler.handleEndCompositeType(name, metaInfo, value);
// Since we are complete, decrement the depth by one.
decrDepth();
}
// TODO MGH - added check to avoid a NPE later in the code.
private void handleCollectionType(String name, Class<?> type, Object value)
throws ColumnNotFoundException, ColumnValueNotAvailableException,
NullPointerException {
if (null == type) {
throw new NullPointerException(
"'type' argument for the collection to introspect is null, can't introspect.");
}
// For a null object, there is no special processing. Hence return
// immediately.
final String typeName = type.getCanonicalName();
if (value == null) {
this.dataHandler.handleStartCollectionType(name, typeName, value);
this.dataHandler.handleEndCollectionType(name, typeName, value);
return;
}
// Increment the depth by 1 and check against the required depth.
boolean shallow = isRequiredDepthReached(true);
if (!shallow) {
boolean isArray = type.isArray();
ResultSet result = (ResultSet) value;
this.dataHandler.handleStartCollectionType(name, typeName, value);
IntrospectionResult[] types = result.getIntrospectionResult();
for (int i = 0; i < types.length; i++) {
String elemName = QueryDataExportHandler.COLLECTION_ELEMENT_NAME;
Collection<?> data = null;
// In case of Arrays of primitive types, the collection can not have
// elements of
// multiple types. Here we get the complete results so as to accommodate
// for the
// Java auto-boxing feature. (We need to specify the correct element
// type).
if (isArray && types[i].getJavaType().isPrimitive()) {
data = result.getQueryResult();
} else {
data = result.getQueryResult(types[i]);
}
Iterator<?> iter = data.iterator();
while (iter.hasNext()) {
Object val = iter.next();
this.dataHandler
.handleStartCollectionElement(elemName, types[i], val);
exportObject(QueryDataExportHandler.COLLECTION_ELEMENT_NAME,
types[i], val);
this.dataHandler.handleEndCollectionElement(elemName);
}
}
this.dataHandler.handleEndCollectionType(name, typeName, value);
} else {
this.dataHandler.handlePrimitiveType(name, type, value);
}
// Since we are complete, decrement the depth by one.
decrDepth();
}
private void handleStructType(String nm, IntrospectionResult metaInfo,
Object val) throws ColumnNotFoundException,
ColumnValueNotAvailableException {
// Increment the depth by 1 and check against the required depth.
boolean shallow = isRequiredDepthReached(true);
String name = (null != nm) ? nm
: QueryDataExportHandler.STRUCT_ELEMENT_NAME;
this.dataHandler.handleStartStructType(name, metaInfo, val);
for (int k = 0; k < metaInfo.getColumnCount(); k++) {
Object colValue = metaInfo.getColumnValue(val, k);
int columnType = metaInfo.getColumnType(val, k);
String colName = metaInfo.getColumnName(k);
Class<?> colClass = metaInfo.getColumnClass(val, k);
// If shallow = true and colClass is not primitive, then we will treat
// this as a primitive type.
// This will stop the subsequent navigation of the object tree.
if ((shallow)
|| (columnType == IntrospectionResult.PRIMITIVE_TYPE_COLUMN)) {
this.dataHandler.handlePrimitiveType(colName, colClass, colValue);
} else if (columnType == IntrospectionResult.COMPOSITE_TYPE_COLUMN) {
IntrospectionResult meta = IntrospectionRepository.singleton()
.getIntrospectionResult(colClass);
handleCompositeType(colName, meta, colValue);
} // This will take care for objects of type (Collection, Map and Array).
else if (columnType == IntrospectionResult.COLLECTION_TYPE_COLUMN) {
ResultSet temp = (ResultSet) colValue;
handleCollectionType(colName, colClass, temp);
temp.close();
} else if (columnType == IntrospectionResult.UNKNOWN_TYPE_COLUMN) {
this.dataHandler.handlePrimitiveType(colName, colClass, colValue);
} else if (columnType == IntrospectionResult.PDX_TYPE_COLUMN) {
IntrospectionResult colMetaInfo = null;
try {
colMetaInfo = IntrospectionRepository.singleton().introspectTypeByObject(colValue);
handlePdxType(colName, colMetaInfo, colValue);
} catch (IntrospectionException e) {
colMetaInfo = null;
LogUtil.warning("Could not introspect: "+colValue+", skipping it..", e);
}
}
}
this.dataHandler.handleEndStructType(name, metaInfo, val);
// Since we are complete, decrement the depth by one.
decrDepth();
}
/**
* Handles exporting for objects of GemFire type PdxInstanceImpl in the tree
* model.
*
* @param name
* name to be used for start node for the given pdxInstanceObj
* @param metaInfo
* IntrospectionResult for the given pdxInstanceObj
* @param pdxInstanceObj
* PdxInstanceImpl object to be introspected
* @throws ColumnNotFoundException
* if column/field at given index is not found
* @throws ColumnValueNotAvailableException
* if value for column/field at given index is not found
*/
/* PdxInstance could also be part of a Collection/Map, Struct, Composite. This
* method (handlePdxType) is called from handleXXX methods for Struct &
* Composite. The handleXXX method for Collection calls exportObject() for
* individual elements and exportObject() calls handlePdxType if an element
* is a PdxInstance */
private void handlePdxType(String name, IntrospectionResult metaInfo, Object pdxInstanceObj)
throws ColumnNotFoundException, ColumnValueNotAvailableException {
// Increment the depth by 1 and check against the required depth.
boolean shallow = isRequiredDepthReached(true);
String nameOpt = (null != name) ? name : metaInfo.getJavaTypeName();
// Start of the Composite object.
this.dataHandler.handleStartPdxType(nameOpt, metaInfo, pdxInstanceObj);
// This object is not null. For a null object, there is no special
// processing.
if (pdxInstanceObj != null) {
for (int k = 0; k < metaInfo.getColumnCount(); k++) {
int columnType = metaInfo.getColumnType(pdxInstanceObj, k);
String colName = metaInfo.getColumnName(k);
Object colValue = metaInfo.getColumnValue(pdxInstanceObj, k);
Class<?> colClass = metaInfo.getColumnClass(pdxInstanceObj, k);
// If shallow = true and colClass is not primitive, then we will treat
// this as a primitive type.
// This will stop the subsequent navigation of the object tree.
if ((shallow) ||
(columnType == IntrospectionResult.PRIMITIVE_TYPE_COLUMN)) {
this.dataHandler.handlePrimitiveType(colName, colClass, colValue);
// System.out.println("QueryResultExporter.handlePdxType()");
} else if (columnType == IntrospectionResult.COLLECTION_TYPE_COLUMN) {
// System.out.println("QueryResultExporter.handlePdxType() : isCollectionType1");
handleSimpleCollectionType(colName, colClass, colValue);
} else if (columnType == IntrospectionResult.PDX_OBJECT_TYPE_COLUMN) {
// LogUtil.info("QueryResultExporter.handlePdxType() : value :: "+value);
//for a PDX_OBJECT_TYPE_COLUMN ignore colClass & use colValue.getClass()
Class<?> colValueClass = Object.class;
if (colValue != null) {
colValueClass = colValue.getClass();//TODO: Just to avoid NPE???? Make it better
}
if (QueryUtil.isPdxInstanceType(colValueClass)) {
IntrospectionResult nestedObjMetaInfo;
try {
nestedObjMetaInfo = IntrospectionRepository.singleton().introspectTypeByObject(colValue);
handlePdxType(colName, nestedObjMetaInfo, colValue);
} catch (IntrospectionException e) {
LogUtil.warning("Could not introspect: "+colValue+", skipping it..", e);
}
} else if (QueryUtil.isCollectionType(colValueClass)) {
//it was probably a collection/array serialized as Object using writeObject()
handleSimpleCollectionType(colName, colValueClass, colValue);
} else if (QueryUtil.isMapType(colValueClass)) {
//it was probably a map serialized as Object using writeObject()
Map<?,?> valuesMap = (Map<?,?>) colValue;
Set<?> entrySet = valuesMap.entrySet();
handleSimpleCollectionType(colName, entrySet.getClass(), entrySet);
} else {
introspectAndHandleElement(colName, colValue);
}
}
}
}
this.dataHandler.handleEndPdxType(name, metaInfo, pdxInstanceObj);
// Since we are complete, decrement the depth by one.
decrDepth();
}
/**
* Handles exporting for the given collection/array/map and objects in it the
* tree model. handleCollectionType works on ResultSet but this method uses
* the actual collection/array/map.
*
* @param name
* name to be used for start node for the given collectionObj
* @param type
* type (java.lang.Class) of the given collection
* @param collectionObj
* instance of one of collection/array/map
* @throws ColumnNotFoundException
* if column/field at given index is not found
* @throws ColumnValueNotAvailableException
* if value for column/field at given index is not found
*/
private void handleSimpleCollectionType(String name, Class<?> type, Object collectionObj)
throws ColumnNotFoundException, ColumnValueNotAvailableException {
if (null == type) {
throw new NullPointerException(
"'type' for the collection to introspect is null, can't introspect.");
}
// For a null object, there is no special processing. Hence return
// immediately.
final String typeName = type.getCanonicalName();
if (collectionObj == null) {
this.dataHandler.handleStartCollectionType(name, typeName, collectionObj);
this.dataHandler.handleEndCollectionType(name, typeName, collectionObj);
return;
}
// Increment the depth by 1 and check against the required depth.
boolean shallow = isRequiredDepthReached(true);
if (!shallow) {
boolean isArray = type.isArray();
this.dataHandler.handleStartCollectionType(name, typeName, collectionObj);
if (isArray) {
Class<?> componentType = type.getComponentType();
int length = Array.getLength(collectionObj);
if (componentType.isPrimitive()) {
//Not using exportObject() to handle here, as it's simple for primitive types
if (boolean.class == componentType) {
for (int i = 0; i < length; i++) {
this.dataHandler.handlePrimitiveType(QueryDataExportHandler.COLLECTION_ELEMENT_NAME, componentType, Array.getBoolean(collectionObj, i));
}
} else if (byte.class == componentType) {
for (int i = 0; i < length; i++) {
this.dataHandler.handlePrimitiveType(QueryDataExportHandler.COLLECTION_ELEMENT_NAME, componentType, Array.getByte(collectionObj, i));
}
} else if (char.class == componentType) {
for (int i = 0; i < length; i++) {
this.dataHandler.handlePrimitiveType(QueryDataExportHandler.COLLECTION_ELEMENT_NAME, componentType, Array.getChar(collectionObj, i));
}
} else if (short.class == componentType) {
for (int i = 0; i < length; i++) {
this.dataHandler.handlePrimitiveType(QueryDataExportHandler.COLLECTION_ELEMENT_NAME, componentType, Array.getShort(collectionObj, i));
}
} else if (int.class == componentType) {
for (int i = 0; i < length; i++) {
this.dataHandler.handlePrimitiveType(QueryDataExportHandler.COLLECTION_ELEMENT_NAME, componentType, Array.getInt(collectionObj, i));
}
} else if (long.class == componentType) {
for (int i = 0; i < length; i++) {
this.dataHandler.handlePrimitiveType(QueryDataExportHandler.COLLECTION_ELEMENT_NAME, componentType, Array.getLong(collectionObj, i));
}
} else if (float.class == componentType) {
for (int i = 0; i < length; i++) {
this.dataHandler.handlePrimitiveType(QueryDataExportHandler.COLLECTION_ELEMENT_NAME, componentType, Array.getFloat(collectionObj, i));
}
} else if (double.class == componentType) {
for (int i = 0; i < length; i++) {
this.dataHandler.handlePrimitiveType(QueryDataExportHandler.COLLECTION_ELEMENT_NAME, componentType, Array.getDouble(collectionObj, i));
}
}
} else {
Object[] arr = (Object[]) collectionObj;
for (int i = 0; i < length; i++) {
introspectAndHandleElement(QueryDataExportHandler.COLLECTION_ELEMENT_NAME, arr[i]);
}
}
} else if (QueryUtil.isCollectionType(type)) {//double check for collection type?
Collection<?> valuesCollection = (Collection<?>) collectionObj;
for (Iterator<?> it = valuesCollection.iterator(); it.hasNext();) {
introspectAndHandleElement(QueryDataExportHandler.COLLECTION_ELEMENT_NAME, it.next());
}
}
this.dataHandler.handleEndCollectionType(name, typeName, collectionObj);
} else {
this.dataHandler.handlePrimitiveType(name, type, collectionObj);
}
}
/**
* Introspects and handles exporting for the given data object in it the tree
* model.
*
* @param data
* object to be introspected
* @throws ColumnNotFoundException
* if column/field at given index is not found
* @throws ColumnValueNotAvailableException
* if value for column/field at given index is not found
*/
private void introspectAndHandleElement(String treeNodeName, Object data)
throws ColumnNotFoundException, ColumnValueNotAvailableException {
IntrospectionRepository singleton = IntrospectionRepository.singleton();
IntrospectionResult dataElementMetaInfo = null;
Object dataElement = data != null ? data : new Object();
try {
dataElementMetaInfo = singleton.introspectTypeByObject(dataElement);
exportObject(treeNodeName, dataElementMetaInfo, dataElement);
} catch (IntrospectionException e) {
LogUtil.warning("Could not introspect: "+data+", skipping it..", e);
//return after logging exception
}
}
private boolean isRequiredDepthReached(boolean incrDepth) {
if (incrDepth) {
depth = depth + 1;
}
if (depthRequired == -1) {
return false;
}
return (depth < depthRequired) ? false : true;
}
private void decrDepth() {
if (depth > 0) {
depth = depth - 1;
}
}
}