blob: 9b79d5ea168fbb5596e11d6fec2b028dd14b34a2 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.myfaces.util;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.HashSet;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.el.MethodExpression;
import javax.el.ValueExpression;
import javax.faces.FacesException;
import javax.faces.component.UICommand;
import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.component.UIViewRoot;
import javax.faces.component.ValueHolder;
import javax.faces.context.FacesContext;
import javax.faces.event.FacesListener;
import javax.faces.validator.Validator;
/**
* Utilities for logging.
*
* @author Manfred Geiler (latest modification by $Author$)
* @version $Revision$ $Date$
*/
public class DebugUtils
{
private static final Logger log = Logger.getLogger(DebugUtils.class.getName());
// Attributes that should not be printed
private static final HashSet<String> IGNORE_ATTRIBUTES;
static
{
IGNORE_ATTRIBUTES = new HashSet<String>();
IGNORE_ATTRIBUTES.add("attributes");
IGNORE_ATTRIBUTES.add("children");
IGNORE_ATTRIBUTES.add("childCount");
IGNORE_ATTRIBUTES.add("class");
IGNORE_ATTRIBUTES.add("facets");
IGNORE_ATTRIBUTES.add("facetsAndChildren");
IGNORE_ATTRIBUTES.add("parent");
IGNORE_ATTRIBUTES.add("actionListeners");
IGNORE_ATTRIBUTES.add("valueChangeListeners");
IGNORE_ATTRIBUTES.add("validators");
IGNORE_ATTRIBUTES.add("rowData");
IGNORE_ATTRIBUTES.add("javax.faces.webapp.COMPONENT_IDS");
IGNORE_ATTRIBUTES.add("javax.faces.webapp.FACET_NAMES");
IGNORE_ATTRIBUTES.add("javax.faces.webapp.CURRENT_VIEW_ROOT");
}
private static final String JSF_COMPONENT_PACKAGE = "javax.faces.component.";
private static final String MYFACES_COMPONENT_PACKAGE = "org.apache.myfaces.component.";
private DebugUtils()
{
// hide from public access
}
public static void assertError(boolean condition, Logger logger, String msg) throws FacesException
{
if (!condition)
{
logger.severe(msg);
throw new FacesException(msg);
}
}
public static void assertFatal(boolean condition, Logger logger, String msg) throws FacesException
{
if (!condition)
{
logger.severe(msg);
throw new FacesException(msg);
}
}
public static void traceView(String additionalMsg)
{
if (log.isLoggable(Level.FINEST))
{
FacesContext facesContext = FacesContext.getCurrentInstance();
if (facesContext == null)
{
log.severe("Cannot not print view to console (no FacesContext).");
return;
}
UIViewRoot viewRoot = facesContext.getViewRoot();
if (viewRoot == null)
{
log.severe("Cannot not print view to console (no ViewRoot in FacesContext).");
return;
}
traceView(additionalMsg, viewRoot);
}
}
/**
* Be careful, when using this version of traceView: Some component properties (e.g. getRenderer) assume, that there
* is a valid viewRoot set in the FacesContext!
*
* @param additionalMsg
* @param viewRoot
*/
private static void traceView(String additionalMsg, UIViewRoot viewRoot)
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos);
if (additionalMsg != null)
{
ps.println(additionalMsg);
}
ps.println("========================================");
printView(viewRoot, ps);
ps.println("========================================");
ps.close();
log.finest(baos.toString());
}
public static void printView(UIViewRoot uiViewRoot, PrintStream stream)
{
printComponent(uiViewRoot, stream, 0, true, null);
}
public static void printComponent(UIComponent comp, PrintStream stream)
{
printComponent(comp, stream, 0, false, null);
}
private static void printComponent(UIComponent comp, PrintStream stream, int indent, boolean withChildrenAndFacets,
String facetName)
{
printIndent(stream, indent);
stream.print('<');
String compType = comp.getClass().getName();
if (compType.startsWith(JSF_COMPONENT_PACKAGE))
{
compType = compType.substring(JSF_COMPONENT_PACKAGE.length());
}
else if (compType.startsWith(MYFACES_COMPONENT_PACKAGE))
{
compType = compType.substring(MYFACES_COMPONENT_PACKAGE.length());
}
stream.print(compType);
printAttribute(stream, "id", comp.getId());
if (facetName != null)
{
printAttribute(stream, "facetName", facetName);
}
for (Map.Entry<String, Object> entry : comp.getAttributes().entrySet())
{
if (!"id".equals(entry.getKey()))
{
printAttribute(stream, entry.getKey(), entry.getValue());
}
}
// HACK: comp.getAttributes() only returns attributes, that are NOT backed
// by a corresponding component property. So, we must explicitly get the
// available properties by Introspection:
BeanInfo beanInfo;
try
{
beanInfo = Introspector.getBeanInfo(comp.getClass());
}
catch (IntrospectionException e)
{
throw new RuntimeException(e);
}
if (!compType.startsWith("org.apache.myfaces.view.facelets.compiler"))
{
PropertyDescriptor propDescriptors[] = beanInfo.getPropertyDescriptors();
for (int i = 0; i < propDescriptors.length; i++)
{
if (propDescriptors[i].getReadMethod() != null)
{
String name = propDescriptors[i].getName();
if (!"id".equals(name))
{
ValueExpression ve = comp.getValueExpression(name);
if (ve != null)
{
printAttribute(stream, name, ve.getExpressionString());
}
else
{
if (name.equals("value") && comp instanceof ValueHolder)
{
// -> localValue
}
else if (!IGNORE_ATTRIBUTES.contains(name))
{
try
{
Object value = comp.getAttributes().get(name);
printAttribute(stream, name, value);
}
catch (Exception e)
{
log.log(Level.SEVERE, e.getMessage() , e);
printAttribute(stream, name, null);
}
}
}
}
}
}
}
boolean mustClose = true;
boolean nestedObjects = false;
if (comp instanceof UICommand)
{
FacesListener[] listeners = ((UICommand)comp).getActionListeners();
if (listeners != null && listeners.length > 0)
{
nestedObjects = true;
stream.println('>');
mustClose = false;
for (int i = 0; i < listeners.length; i++)
{
FacesListener listener = listeners[i];
printIndent(stream, indent + 1);
stream.print('<');
stream.print(listener.getClass().getName());
stream.println("/>");
}
}
}
if (comp instanceof UIInput)
{
FacesListener[] listeners = ((UIInput)comp).getValueChangeListeners();
if (listeners != null && listeners.length > 0)
{
nestedObjects = true;
stream.println('>');
mustClose = false;
for (int i = 0; i < listeners.length; i++)
{
FacesListener listener = listeners[i];
printIndent(stream, indent + 1);
stream.print('<');
stream.print(listener.getClass().getName());
stream.println("/>");
}
}
Validator[] validators = ((UIInput)comp).getValidators();
if (validators != null && validators.length > 0)
{
nestedObjects = true;
stream.println('>');
mustClose = false;
for (int i = 0; i < validators.length; i++)
{
Validator validator = validators[i];
printIndent(stream, indent + 1);
stream.print('<');
stream.print(validator.getClass().getName());
stream.println("/>");
}
}
}
if (withChildrenAndFacets)
{
int childCount = comp.getChildCount();
if (childCount > 0 || comp.getFacetCount() > 0)
{
Map<String, UIComponent> facetsMap = comp.getFacets();
nestedObjects = true;
if (mustClose)
{
stream.println('>');
mustClose = false;
}
if (childCount > 0)
{
for (int i = 0; i < childCount; i++)
{
UIComponent child = comp.getChildren().get(i);
printComponent(child, stream, indent + 1, true, null);
}
}
for (Map.Entry<String, UIComponent> entry : facetsMap.entrySet())
{
printComponent(entry.getValue(), stream, indent + 1, true, entry.getKey());
}
}
}
if (nestedObjects)
{
if (mustClose)
{
stream.println("/>");
}
else
{
printIndent(stream, indent);
stream.print("</");
stream.print(compType);
stream.println('>');
}
}
else
{
stream.println("/>");
}
}
private static void printAttribute(PrintStream stream, String name, Object value)
{
if (IGNORE_ATTRIBUTES.contains(name))
{
return;
}
if (name.startsWith("javax.faces.webapp.UIComponentTag."))
{
name = name.substring("javax.faces.webapp.UIComponentTag.".length());
}
stream.print(' ');
stream.print(name);
stream.print("=\"");
if (value != null)
{
if (value instanceof UIComponent)
{
stream.print("[id:");
stream.print(((UIComponent)value).getId());
stream.print(']');
}
else if (value instanceof MethodExpression)
{
stream.print(((MethodExpression)value).getExpressionString());
}
else
{
stream.print(value.toString());
}
}
else
{
stream.print("NULL");
}
stream.print('"');
}
private static void printIndent(PrintStream stream, int depth)
{
for (int i = 0; i < depth; i++)
{
stream.print(" ");
}
}
public static String componentAsString(UIComponent comp)
{
try
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
printComponent(comp, new PrintStream(baos));
baos.close();
return baos.toString();
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
}