/************************************************************************************** * Copyright (C) 2008 EsperTech, Inc. All rights reserved. * * http://esper.codehaus.org * * http://www.espertech.com * * ---------------------------------------------------------------------------------- * * The software in this package is published under the terms of the GPL license * * a copy of which has been included with this distribution in the license.txt file. * **************************************************************************************/ package com.espertech.esper.view; import com.espertech.esper.collection.UniformPair; import com.espertech.esper.client.EventBean; import com.espertech.esper.util.ExecutionPathDebugLog; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Stack; /** * A helper class for View implementations that provides generic implementation for some of the methods. * Methods that contain the actual logic of the view are not implemented in this class. * A common implementation normally does not need to override any of the methods implemented here, their * implementation is generic and should suffice. * The class provides a convenience method for updateing it's children data updateChildren(Object[], Object[]). * This method should be called from within the View.update(Object[], Object[]) methods in the subclasses. */ public abstract class ViewSupport implements View { /** * Parent viewable to this view - directly accessible by subclasses. */ protected Viewable parent; private final ArrayList<View> children; /** * Constructor. */ protected ViewSupport() { children = new ArrayList<View>(); } public Viewable getParent() { return parent; } public void setParent(Viewable parent) { this.parent = parent; } public View addView(View view) { children.add(view); view.setParent(this); return view; } public boolean removeView(View view) { boolean isRemoved = children.remove(view); view.setParent(null); return isRemoved; } public void removeAllViews() { children.clear(); } public List<View> getViews() { return children; } public boolean hasViews() { return (!children.isEmpty()); } /** * Updates all the children with new data. * Views may want to use the hasViews method on the Viewable interface to determine * if there are any child views attached at all, and save the work of constructing the arrays and * making the call to updateChildren() in case there aren't any children attached. * @param newData is the array of new event data * @param oldData is the array of old event data */ public void updateChildren(EventBean[] newData, EventBean[] oldData) { int size = children.size(); // Provide a shortcut for a single child view since this is a very common case. // No iteration required here. if (size == 0) { return; } if (size == 1) { children.get(0).update(newData, oldData); } else { // since there often is zero or one view underneath, the iteration case is slower for (View child : children) { child.update(newData, oldData); } } } /** * Updates all the children with new data. Static convenience method that accepts the list of child * views as a parameter. * @param childViews is the list of child views to send the data to * @param newData is the array of new event data * @param oldData is the array of old event data */ protected static void updateChildren(Collection<View> childViews, EventBean[] newData, EventBean[] oldData) { for (View child : childViews) { child.update(newData, oldData); } } /** * Convenience method for logging the parameters passed to the update method. Only logs if debug is enabled. * @param prefix is a prefix text to output for each line * @param result is the data in an update call */ public static void dumpUpdateParams(String prefix, UniformPair<EventBean[]> result) { EventBean[] newEventArr = result != null ? result.getFirst() : null; EventBean[] oldEventArr = result != null ? result.getSecond() : null; dumpUpdateParams(prefix, newEventArr, oldEventArr); } /** * Convenience method for logging the parameters passed to the update method. Only logs if debug is enabled. * @param prefix is a prefix text to output for each line * @param newData is the new data in an update call * @param oldData is the old data in an update call */ public static void dumpUpdateParams(String prefix, Object[] newData, Object[] oldData) { if (!log.isDebugEnabled()) { return; } StringWriter buffer = new StringWriter(); PrintWriter writer = new PrintWriter(buffer); if (newData == null) { writer.println(prefix + " newData=null "); } else { writer.println(prefix + " newData.size=" + newData.length + "..."); printObjectArray(prefix, writer, newData); } if (oldData == null) { writer.println(prefix + " oldData=null "); } else { writer.println(prefix + " oldData.size=" + oldData.length + "..."); printObjectArray(prefix, writer, oldData); } } private static void printObjectArray(String prefix, PrintWriter writer, Object[] objects) { int count = 0; for (Object object : objects) { String objectToString = (object == null) ? "null" : object.toString(); writer.println(prefix + " #" + count + " = " + objectToString); } } /** * Convenience method for logging the child views of a Viewable. Only logs if debug is enabled. * This is a recursive method. * @param prefix is a text to print for each view printed * @param parentViewable is the parent for which the child views are displayed. */ public static void dumpChildViews(String prefix, Viewable parentViewable) { if (log.isDebugEnabled()) { if (parentViewable != null && parentViewable.getViews() != null) { for (View child : parentViewable.getViews()) { if ((ExecutionPathDebugLog.isDebugEnabled) && (log.isDebugEnabled())) { log.debug(".dumpChildViews " + prefix + ' ' + child.toString()); } dumpChildViews(prefix + " ", child); } } } } /** * Find the descendent view in the view tree under the parent view returning the list of view nodes * between the parent view and the descendent view. Returns null if the descendent view is not found. * Returns an empty list if the descendent view is a child view of the parent view. * @param parentView is the view to start searching under * @param descendentView is the view to find * @return list of Viewable nodes between parent and descendent view. */ public static List<View> findDescendent(Viewable parentView, Viewable descendentView) { Stack<View> stack = new Stack<View>(); boolean found; for (View view : parentView.getViews()) { if (view == descendentView) { return stack; } found = findDescendentRecusive(view, descendentView, stack); if (found) { return stack; } } return null; } private static boolean findDescendentRecusive(View parentView, Viewable descendentView, Stack<View> stack) { stack.push(parentView); boolean found = false; for (View view : parentView.getViews()) { if (view == descendentView) { return true; } found = findDescendentRecusive(view, descendentView, stack); if (found) { break; } } if (!found) { stack.pop(); return false; } return true; } private static final Log log = LogFactory.getLog(ViewSupport.class); }