You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by ja...@apache.org on 2010/05/04 22:06:31 UTC
svn commit: r941020 - in /myfaces/core/trunk:
api/src/main/java/javax/faces/component/
impl/src/main/java/org/apache/myfaces/config/
impl/src/main/java/org/apache/myfaces/renderkit/
impl/src/main/java/org/apache/myfaces/view/facelets/component/ impl/sr...
Author: jakobk
Date: Tue May 4 20:06:31 2010
New Revision: 941020
URL: http://svn.apache.org/viewvc?rev=941020&view=rev
Log:
MYFACES-2676 Include the whole component tree with all real values in the debug page (first draft of this new feature)
Added:
myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/tag/ui/DebugPhaseListener.java (with props)
Modified:
myfaces/core/trunk/api/src/main/java/javax/faces/component/UIInput.java
myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/FacesConfigurator.java
myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/renderkit/ErrorPageWriter.java
myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/component/UIRepeat.java
myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/webapp/AbstractFacesInitializer.java
myfaces/core/trunk/impl/src/main/resources/META-INF/rsc/myfaces-dev-debug.xml
Modified: myfaces/core/trunk/api/src/main/java/javax/faces/component/UIInput.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/api/src/main/java/javax/faces/component/UIInput.java?rev=941020&r1=941019&r2=941020&view=diff
==============================================================================
--- myfaces/core/trunk/api/src/main/java/javax/faces/component/UIInput.java (original)
+++ myfaces/core/trunk/api/src/main/java/javax/faces/component/UIInput.java Tue May 4 20:06:31 2010
@@ -19,12 +19,17 @@
package javax.faces.component;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
import java.util.Map;
+import javax.el.ELContext;
import javax.el.ValueExpression;
import javax.faces.FacesException;
import javax.faces.application.FacesMessage;
+import javax.faces.application.ProjectStage;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
@@ -42,6 +47,7 @@ import javax.faces.event.ValueChangeEven
import javax.faces.event.ValueChangeListener;
import javax.faces.render.Renderer;
import javax.faces.validator.Validator;
+import javax.faces.webapp.FacesServlet;
import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFComponent;
import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFListener;
@@ -74,9 +80,16 @@ public class UIInput extends UIOutput im
@JSFWebConfigParam(defaultValue="auto", expectedValues="auto, true, false", since="2.0")
public static final String VALIDATE_EMPTY_FIELDS_PARAM_NAME = "javax.faces.VALIDATE_EMPTY_FIELDS";
+
+ /**
+ * Extended debug info is stored under this key in the request
+ * map for every UIInput component when in Development mode.
+ * ATTENTION: this constant is duplicate in org.apache.myfaces.renderkit.ErrorPageWriter
+ */
+ private static final String DEBUG_INFO_KEY = "org.apache.myfaces.debug.DEBUG_INFO";
private static final Validator[] EMPTY_VALIDATOR_ARRAY = new Validator[0];
-
+
private _DeltaList<Validator> _validatorList;
/**
@@ -102,6 +115,12 @@ public class UIInput extends UIOutput im
@Override
public void setValue(Object value)
{
+ if (FacesContext.getCurrentInstance().isProjectStage(ProjectStage.Development))
+ {
+ // extended debug-info when in Development mode
+ _createFieldDebugInfo(FacesContext.getCurrentInstance(), "localValue",
+ getLocalValue(), value, 1);
+ }
setLocalValueSet(true);
super.setValue(value);
}
@@ -537,6 +556,21 @@ public class UIInput extends UIOutput im
setLocalValueSet(false);
setValid(true);
}
+
+ @Override
+ public ValueExpression getValueExpression(String name)
+ {
+ // override this method to be able to install a _DebugValueExpressionWrapper
+
+ ValueExpression valueExpression = super.getValueExpression(name);
+ if (FacesContext.getCurrentInstance().isProjectStage(ProjectStage.Development)
+ && "value".equals(name))
+ {
+ // use a _DebugValueExpressionWrapper for debugging of the value-ValueExpression
+ valueExpression = new _DebugValueExpressionWrapper(valueExpression);
+ }
+ return valueExpression;
+ }
/**
* A boolean value that identifies the phase during which action events should fire.
@@ -768,6 +802,12 @@ public class UIInput extends UIOutput im
public void setSubmittedValue(Object submittedValue)
{
+ if (FacesContext.getCurrentInstance().isProjectStage(ProjectStage.Development))
+ {
+ // extended debug-info when in Development mode
+ _createFieldDebugInfo(FacesContext.getCurrentInstance(), "submittedValue",
+ getSubmittedValue(), submittedValue, 1);
+ }
getStateHelper().put(PropertyKeys.submittedValue, submittedValue );
}
@@ -903,6 +943,110 @@ public class UIInput extends UIOutput im
return saveAttachedState(facesContext,_validatorList);
}
}
+
+ /**
+ * Returns the debug-info Map for this component.
+ * @return
+ */
+ @SuppressWarnings("unchecked")
+ private Map<String, List<String>> _getDebugInfoMap()
+ {
+ final Map<String, Object> requestMap = FacesContext.getCurrentInstance()
+ .getExternalContext().getRequestMap();
+ Map<String, List<String>> debugInfo = (Map<String, List<String>>)
+ requestMap.get(DEBUG_INFO_KEY + getClientId());
+ if (debugInfo == null)
+ {
+ // no debug info available yet, create one and put it on the attributes map
+ debugInfo = new HashMap<String, List<String>>();
+ requestMap.put(DEBUG_INFO_KEY + getClientId(), debugInfo);
+ }
+ return debugInfo;
+ }
+
+ /**
+ * Returns the field's debug-infos from the component's debug-info Map.
+ * @param field
+ * @return
+ */
+ private List<String> _getFieldDebugInfos(final String field)
+ {
+ Map<String, List<String>> debugInfo = _getDebugInfoMap();
+ List<String> fieldDebugInfo = debugInfo.get(field);
+ if (fieldDebugInfo == null)
+ {
+ // no field debug-infos yet, create them and store it in the Map
+ fieldDebugInfo = new ArrayList<String>();
+ debugInfo.put(field, fieldDebugInfo);
+ }
+ return fieldDebugInfo;
+ }
+
+ /**
+ * Creates the field debug-info for the given field, which changed
+ * from oldValue to newValue.
+ *
+ * @param facesContext
+ * @param field
+ * @param oldValue
+ * @param newValue
+ * @param skipStackTaceElements How many StackTraceElements should be skipped
+ * when the calling function will be determined.
+ */
+ private void _createFieldDebugInfo(FacesContext facesContext,
+ final String field, Object oldValue,
+ Object newValue, final int skipStackTaceElements)
+ {
+ if (oldValue == null && newValue == null)
+ {
+ // both values are null, not interesting and can
+ // happen a lot in UIData with saving and restoring state
+ return;
+ }
+
+ // convert Array values into a more readable format
+ if (oldValue != null && oldValue.getClass().isArray())
+ {
+ oldValue = Arrays.deepToString((Object[]) oldValue);
+ }
+ if (newValue != null && newValue.getClass().isArray())
+ {
+ newValue = Arrays.deepToString((Object[]) newValue);
+ }
+
+ StringBuilder sb = new StringBuilder();
+ sb.append("<b>");
+ sb.append(field);
+ sb.append("</b> set from <b>");
+ sb.append(oldValue);
+ sb.append("</b> to <b>");
+ sb.append(newValue);
+ sb.append("</b> in Phase ");
+ sb.append(facesContext.getCurrentPhaseId());
+ sb.append(" with call from:<ul>");
+
+ // use Throwable to get the current call stack
+ Throwable throwableHelper = new Throwable();
+ StackTraceElement[] stackTraceElements = throwableHelper.getStackTrace();
+ // + 1 because this method should also be skipped
+ for (int i = skipStackTaceElements + 1; i < stackTraceElements.length; i++)
+ {
+ sb.append("<li>");
+ sb.append(stackTraceElements[i].toString());
+ sb.append("</li>");
+
+ if (FacesServlet.class.getCanonicalName()
+ .equals(stackTraceElements[i].getClassName()))
+ {
+ // stop after the FacesServlet
+ break;
+ }
+ }
+ sb.append("</ul>");
+
+ // add the debug info
+ _getFieldDebugInfos(field).add(sb.toString());
+ }
/**
* Check if a value is empty or not. Since we don't know the class of
@@ -948,4 +1092,83 @@ public class UIInput extends UIOutput im
}
return false;
}
+
+ /**
+ * This ValueExpression wrapper is installed for debugging reasons when
+ * in Development mode. Every call to setValue() will be forwarded
+ * to _createFieldDebugInfo() in order to create debugging infos.
+ *
+ * @author Jakob Korherr
+ */
+ private class _DebugValueExpressionWrapper extends ValueExpression
+ {
+
+ private static final long serialVersionUID = -7114362926319263354L;
+
+ private ValueExpression _wrapped;
+
+ _DebugValueExpressionWrapper(ValueExpression wrapped)
+ {
+ _wrapped = wrapped;
+ }
+
+ @Override
+ public Class<?> getExpectedType()
+ {
+ return _wrapped.getExpectedType();
+ }
+
+ @Override
+ public Class<?> getType(ELContext ctx)
+ {
+ return _wrapped.getType(ctx);
+ }
+
+ @Override
+ public Object getValue(ELContext ctx)
+ {
+ return _wrapped.getValue(ctx);
+ }
+
+ @Override
+ public boolean isReadOnly(ELContext ctx)
+ {
+ return _wrapped.isReadOnly(ctx);
+ }
+
+ @Override
+ public void setValue(ELContext ctx, Object value)
+ {
+ // create debug info for this call to setValue
+ _createFieldDebugInfo((FacesContext) ctx.getContext(FacesContext.class),
+ "value", _wrapped.getValue(ctx), value, 2);
+
+ _wrapped.setValue(ctx, value);
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ return _wrapped.equals(o);
+ }
+
+ @Override
+ public String getExpressionString()
+ {
+ return _wrapped.getExpressionString();
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return _wrapped.hashCode();
+ }
+
+ @Override
+ public boolean isLiteralText()
+ {
+ return _wrapped.isLiteralText();
+ }
+
+ }
}
Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/FacesConfigurator.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/FacesConfigurator.java?rev=941020&r1=941019&r2=941020&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/FacesConfigurator.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/FacesConfigurator.java Tue May 4 20:06:31 2010
@@ -55,6 +55,7 @@ import javax.faces.application.Applicati
import javax.faces.application.ApplicationFactory;
import javax.faces.application.ConfigurableNavigationHandler;
import javax.faces.application.NavigationHandler;
+import javax.faces.application.ProjectStage;
import javax.faces.application.ResourceHandler;
import javax.faces.application.StateManager;
import javax.faces.application.ViewHandler;
@@ -116,6 +117,7 @@ import org.apache.myfaces.util.Container
import org.apache.myfaces.util.ExternalSpecifications;
import org.apache.myfaces.view.ViewDeclarationLanguageFactoryImpl;
import org.apache.myfaces.view.facelets.tag.jsf.TagHandlerDelegateFactoryImpl;
+import org.apache.myfaces.view.facelets.tag.ui.DebugPhaseListener;
import org.apache.myfaces.view.facelets.util.Classpath;
import org.xml.sax.SAXException;
@@ -2374,6 +2376,15 @@ public class FacesConfigurator
log.severe("Class " + listenerClassName + " does not implement PhaseListener");
}
}
+
+ // if ProjectStage is Development, install the DebugPhaseListener
+ // Note that FacesContext.getCurrentInstance() will be null for the very first
+ // initialisation here. In that case the PhaseListener is installed in AbstractFacesInitializer.
+ FacesContext facesContext = FacesContext.getCurrentInstance();
+ if (facesContext != null && facesContext.isProjectStage(ProjectStage.Development))
+ {
+ lifecycle.addPhaseListener(new DebugPhaseListener());
+ }
}
private String getLifecycleId()
Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/renderkit/ErrorPageWriter.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/renderkit/ErrorPageWriter.java?rev=941020&r1=941019&r2=941020&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/renderkit/ErrorPageWriter.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/renderkit/ErrorPageWriter.java Tue May 4 20:06:31 2010
@@ -34,6 +34,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
+import java.util.EnumSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -47,20 +48,29 @@ import java.util.regex.Pattern;
import javax.el.Expression;
import javax.el.ValueExpression;
import javax.faces.FacesException;
+import javax.faces.component.UIColumn;
import javax.faces.component.UIComponent;
+import javax.faces.component.UIData;
+import javax.faces.component.UIInput;
import javax.faces.component.UIViewRoot;
+import javax.faces.component.visit.VisitCallback;
+import javax.faces.component.visit.VisitContext;
+import javax.faces.component.visit.VisitHint;
+import javax.faces.component.visit.VisitResult;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.context.PartialResponseWriter;
import javax.faces.context.ResponseWriter;
import javax.faces.el.MethodBinding;
import javax.faces.el.ValueBinding;
+import javax.faces.render.Renderer;
import javax.servlet.http.HttpServletResponse;
import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
import org.apache.myfaces.shared_impl.renderkit.html.HtmlResponseWriterImpl;
import org.apache.myfaces.shared_impl.util.ClassUtils;
import org.apache.myfaces.shared_impl.webapp.webxml.WebXml;
+import org.apache.myfaces.view.facelets.component.UIRepeat;
/**
* This class provides utility methods to generate the
@@ -197,6 +207,20 @@ public final class ErrorPageWriter
private final static String[] IGNORE = new String[] { "parent", "rendererType" };
/**
+ * Extended debug info is stored under this key in the request
+ * map for every UIInput component when in Development mode.
+ * ATTENTION: this constant is duplicate in javax.faces.component.UIInput
+ */
+ public static final String DEBUG_INFO_KEY = "org.apache.myfaces.debug.DEBUG_INFO";
+
+ /**
+ * The number of facets of this component which have already been visited while
+ * creating the extended component tree is saved under this key in the component's
+ * attribute map.
+ */
+ private static final String VISITED_FACET_COUNT_KEY = "org.apache.myfaces.debug.VISITED_FACET_COUNT";
+
+ /**
* Indicate if myfaces is responsible to handle errors.
* See http://wiki.apache.org/myfaces/Handling_Server_Errors for details.
*/
@@ -294,6 +318,10 @@ public final class ErrorPageWriter
{
_writeComponent(writer, faces.getViewRoot(), null);
}
+ else if ("extendedtree".equals(DEBUG_PARTS[i]))
+ {
+ _writeExtendedComponentTree(writer, faces);
+ }
else if ("vars".equals(DEBUG_PARTS[i]))
{
_writeVariables(writer, faces, faces.getViewRoot());
@@ -601,7 +629,7 @@ public final class ErrorPageWriter
boolean hasChildren = c.getChildCount() > 0 || c.getFacets().size() > 0;
- _writeStart(writer, c, hasChildren);
+ _writeStart(writer, c, hasChildren, true);
writer.write("</dt>");
if (hasChildren)
{
@@ -632,6 +660,289 @@ public final class ErrorPageWriter
}
writer.write("</dl>");
}
+
+ /**
+ * Creates the Extended Component Tree via UIViewRoot.visitTree()
+ * and ExtendedComponentTreeVisitCallback as VisitCallback.
+ *
+ * @param writer
+ * @param facesContext
+ * @throws IOException
+ */
+ private static void _writeExtendedComponentTree(Writer writer,
+ FacesContext facesContext) throws IOException
+ {
+ VisitContext visitContext = VisitContext.createVisitContext(
+ facesContext, null, EnumSet.of(VisitHint.SKIP_UNRENDERED));
+ facesContext.getViewRoot().visitTree(visitContext, new ExtendedComponentTreeVisitCallback(writer));
+ }
+
+ /**
+ * The VisitCallback that is used to create the Extended Component Tree.
+ *
+ * @author Jakob Korherr
+ */
+ private static class ExtendedComponentTreeVisitCallback implements VisitCallback
+ {
+
+ private Writer _writer;
+
+ public ExtendedComponentTreeVisitCallback(Writer writer)
+ {
+ _writer = writer;
+ }
+
+ @SuppressWarnings("unchecked")
+ public VisitResult visit(VisitContext context, UIComponent target)
+ {
+ final Map<String, Object> requestMap = context.getFacesContext()
+ .getExternalContext().getRequestMap();
+
+ try
+ {
+ if (!(target instanceof UIViewRoot))
+ {
+ _writer.write("<dd>");
+ }
+
+ UIComponent parent = target.getParent();
+ boolean hasChildren = (target.getChildCount() > 0 || target.getFacets().size() > 0);
+ String facetName = _getFacetName(target);
+
+ if (!(target instanceof UIColumn))
+ {
+ if (parent instanceof UIColumn
+ && (parent.getChildren().get(0) == target
+ || (facetName != null &&_getVisitedFacetCount(parent) == 0)))
+ {
+ if (parent.getParent() instanceof UIData
+ && _isFirstUIColumn(parent.getParent(), (UIColumn) parent))
+ {
+ _writer.write("<span>Row: ");
+ int rowIndex = ((UIData) parent.getParent()).getRowIndex();
+ _writer.write("" + rowIndex);
+ if (rowIndex == -1)
+ {
+ // tell the user that rowIndex == -1 stands for visiting column-facets
+ _writer.write(" (all column facets)");
+ }
+ _writer.write("</span>");
+ }
+ _writer.write("<dl><dt>");
+ _writeStart(_writer, parent, true, false);
+ _writer.write("</dt><dd>");
+ }
+
+ if (facetName != null)
+ {
+ _writer.write("<span>" + facetName + "</span>");
+ _incrementVisitedFacetCount(parent);
+ }
+ _writer.write("<dl><dt");
+ if (_isText(target))
+ {
+ _writer.write(" class=\"uicText\"");
+ }
+ _writer.write(">");
+
+ Map<String, List<String>> debugInfo = null;
+ // is the target a UIInput component?
+ if (target instanceof UIInput)
+ {
+ // get the debug info
+ debugInfo = (Map<String, List<String>>) requestMap
+ .get(DEBUG_INFO_KEY + target.getClientId());
+ }
+
+ // Get the component's renderer.
+ // Note that getRenderer(FacesContext context) is definded in UIComponent,
+ // but it is protected, so we have to use reflection!
+ Renderer renderer = null;
+ try
+ {
+ Method getRenderer = UIComponent.class.getDeclaredMethod(
+ "getRenderer", FacesContext.class);
+ // make it accessible for us!
+ getRenderer.setAccessible(true);
+ renderer = (Renderer) getRenderer.invoke(target, context.getFacesContext());
+ }
+ catch (Exception e)
+ {
+ // nothing - do not output renderer information
+ }
+
+ // write the component start
+ _writeStart(_writer, target, (hasChildren || debugInfo != null || renderer != null), false);
+ _writer.write("</dt>");
+
+ if (renderer != null)
+ {
+ // write renderer info
+ _writer.write("<div class=\"renderer\">Rendered by ");
+ _writer.write(renderer.getClass().getCanonicalName());
+ _writer.write("</div>");
+
+ if (!hasChildren && debugInfo == null)
+ {
+ // close the component
+ _writer.write("<dt>");
+ _writeEnd(_writer, target);
+ _writer.write("</dt>");
+ }
+ }
+
+ if (debugInfo != null)
+ {
+ final String fieldid = target.getClientId() + "_lifecycle";
+ _writer.write("<div class=\"lifecycle_values_wrapper\">");
+ _writer.write("<a href=\"#\" onclick=\"toggle('");
+ _writer.write(fieldid);
+ _writer.write("'); return false;\"><span id=\"");
+ _writer.write(fieldid);
+ _writer.write("Off\">+</span><span id=\"");
+ _writer.write(fieldid);
+ _writer.write("On\" style=\"display: none;\">-</span> Value Lifecycle</a>");
+ _writer.write("<div id=\"");
+ _writer.write(fieldid);
+ _writer.write("\" class=\"lifecycle_values\">");
+
+ // process any available debug info
+ for (Map.Entry<String, List<String>> entry : debugInfo.entrySet())
+ {
+ _writer.write("<span>");
+ _writer.write(entry.getKey());
+ _writer.write("</span><ol>");
+ for (String change : entry.getValue())
+ {
+ _writer.write("<li>");
+ _writer.write(change);
+ _writer.write("</li>");
+ }
+ _writer.write("</ol>");
+ }
+
+ _writer.write("</div></div>");
+
+ // now remove the debug info from the request map,
+ // so that it does not appear in the scope values of the debug page
+ requestMap.remove(DEBUG_INFO_KEY + target.getClientId());
+
+ if (!hasChildren)
+ {
+ // close the component
+ _writer.write("<dt>");
+ _writeEnd(_writer, target);
+ _writer.write("</dt>");
+ }
+ }
+ }
+
+ if (!hasChildren)
+ {
+ _writer.write("</dl>");
+
+ while (parent != null
+ && (parent.getChildren().get(parent.getChildCount() - 1) == target
+ || (parent.getFacetCount() != 0
+ && _getVisitedFacetCount(parent) == parent.getFacetCount())))
+ {
+ // target is last child of parent or the "last" facet
+
+ // remove the visited facet count from the attribute map
+ _removeVisitedFacetCount(parent);
+
+ // check for componentes that visit their children multiple times
+ if (parent instanceof UIData)
+ {
+ UIData uidata = (UIData) parent;
+ if (uidata.getRowIndex() != uidata.getRowCount() - 1)
+ {
+ // only continue if we're in the last row
+ break;
+ }
+ }
+ else if (parent instanceof UIRepeat)
+ {
+ UIRepeat uirepeat = (UIRepeat) parent;
+ if (uirepeat.getIndex() + uirepeat.getStep() < uirepeat.getRowCount())
+ {
+ // only continue if we're in the last row
+ break;
+ }
+ }
+
+ _writer.write("</dd><dt>");
+ _writeEnd(_writer, parent);
+ _writer.write("</dt></dl>");
+
+ if (!(parent instanceof UIViewRoot))
+ {
+ _writer.write("</dd>");
+ }
+
+ target = parent;
+ parent = target.getParent();
+ }
+ }
+ }
+ catch (IOException ioe)
+ {
+ throw new FacesException(ioe);
+ }
+
+ return VisitResult.ACCEPT;
+ }
+
+ }
+
+ private static boolean _isFirstUIColumn(UIComponent uidata, UIColumn uicolumn)
+ {
+ for (UIComponent child : uidata.getChildren())
+ {
+ if (child instanceof UIColumn)
+ {
+ return (child == uicolumn);
+ }
+ }
+ return false;
+ }
+
+ private static String _getFacetName(UIComponent component)
+ {
+ UIComponent parent = component.getParent();
+ if (parent != null)
+ {
+ for (Map.Entry<String, UIComponent> entry : parent.getFacets().entrySet())
+ {
+ if (entry.getValue() == component)
+ {
+ return entry.getKey();
+ }
+ }
+ }
+ return null;
+ }
+
+ private static int _getVisitedFacetCount(UIComponent component)
+ {
+ Integer count = (Integer) component.getAttributes().get(VISITED_FACET_COUNT_KEY);
+ if (count != null)
+ {
+ return count;
+ }
+ return 0;
+ }
+
+ private static void _incrementVisitedFacetCount(UIComponent component)
+ {
+ component.getAttributes().put(VISITED_FACET_COUNT_KEY,
+ _getVisitedFacetCount(component) + 1);
+ }
+
+ private static void _removeVisitedFacetCount(UIComponent component)
+ {
+ component.getAttributes().remove(VISITED_FACET_COUNT_KEY);
+ }
private static void _writeEnd(Writer writer, UIComponent c) throws IOException
{
@@ -644,7 +955,7 @@ public final class ErrorPageWriter
}
}
- private static void _writeAttributes(Writer writer, UIComponent c)
+ private static void _writeAttributes(Writer writer, UIComponent c, boolean valueExpressionValues)
{
try
{
@@ -663,7 +974,7 @@ public final class ErrorPageWriter
{
// first check if the property is a ValueExpression
valueExpression = c.getValueExpression(pd[i].getName());
- if (valueExpression != null)
+ if (valueExpressionValues && valueExpression != null)
{
_writeAttribute(writer, pd[i].getName(), valueExpression.getExpressionString());
}
@@ -692,6 +1003,7 @@ public final class ErrorPageWriter
{
str = v.toString();
}
+
_writeAttribute(writer, pd[i].getName(), str);
}
}
@@ -724,7 +1036,8 @@ public final class ErrorPageWriter
writer.write("\"");
}
- private static void _writeStart(Writer writer, UIComponent c, boolean children) throws IOException
+ private static void _writeStart(Writer writer, UIComponent c,
+ boolean children, boolean valueExpressionValues) throws IOException
{
if (_isText(c))
{
@@ -735,7 +1048,7 @@ public final class ErrorPageWriter
{
writer.write(TS);
writer.write(_getName(c));
- _writeAttributes(writer, c);
+ _writeAttributes(writer, c, valueExpressionValues);
if (children)
{
writer.write('>');
Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/component/UIRepeat.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/component/UIRepeat.java?rev=941020&r1=941019&r2=941020&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/component/UIRepeat.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/component/UIRepeat.java Tue May 4 20:06:31 2010
@@ -456,6 +456,23 @@ public class UIRepeat extends UIComponen
}
}
+ /**
+ * Returns the rowCount of the underlying DataModel.
+ * @return
+ */
+ public int getRowCount()
+ {
+ return getDataModel().getRowCount();
+ }
+
+ /**
+ * Returns the current index.
+ */
+ public int getIndex()
+ {
+ return _index;
+ }
+
private void _setIndex(int index)
{
// save child state
Added: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/tag/ui/DebugPhaseListener.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/tag/ui/DebugPhaseListener.java?rev=941020&view=auto
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/tag/ui/DebugPhaseListener.java (added)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/tag/ui/DebugPhaseListener.java Tue May 4 20:06:31 2010
@@ -0,0 +1,92 @@
+/*
+ * 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.view.facelets.tag.ui;
+
+import javax.faces.component.UIComponent;
+import javax.faces.component.UIViewRoot;
+import javax.faces.component.visit.VisitCallback;
+import javax.faces.component.visit.VisitContext;
+import javax.faces.component.visit.VisitResult;
+import javax.faces.event.PhaseEvent;
+import javax.faces.event.PhaseId;
+import javax.faces.event.PhaseListener;
+
+/**
+ * PhaseListener to create extended debug information.
+ * Installed in FacesConfigurator.configureLifecycle() if ProjectStage is Development.
+ *
+ * @author Jakob Korherr (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+public class DebugPhaseListener implements PhaseListener
+{
+
+ private static final long serialVersionUID = -1517198431551012882L;
+
+ /**
+ * VisitCallback used for visitTree()
+ *
+ * @author Jakob Korherr
+ */
+ private class DebugVisitCallback implements VisitCallback
+ {
+
+ public VisitResult visit(VisitContext context, UIComponent target)
+ {
+ // TODO implement for components that do not extend UIInput
+
+ return VisitResult.ACCEPT;
+ }
+
+ }
+
+ private boolean _afterPhase = false;
+ private PhaseId _currentPhase;
+ private DebugVisitCallback _visitCallback = new DebugVisitCallback();
+
+ public void afterPhase(PhaseEvent event)
+ {
+ _doTreeVisit(event, true);
+ }
+
+ public void beforePhase(PhaseEvent event)
+ {
+ _doTreeVisit(event, false);
+ }
+
+ public PhaseId getPhaseId()
+ {
+ return PhaseId.ANY_PHASE;
+ }
+
+ private void _doTreeVisit(PhaseEvent event, boolean afterPhase)
+ {
+ _afterPhase = afterPhase;
+ _currentPhase = event.getPhaseId();
+
+ // visitTree() on the UIViewRoot
+ UIViewRoot viewroot = event.getFacesContext().getViewRoot();
+ if (viewroot != null)
+ {
+ viewroot.visitTree(VisitContext.createVisitContext(
+ event.getFacesContext()), _visitCallback);
+ }
+ }
+
+}
Propchange: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/tag/ui/DebugPhaseListener.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/tag/ui/DebugPhaseListener.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Propchange: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/tag/ui/DebugPhaseListener.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/webapp/AbstractFacesInitializer.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/webapp/AbstractFacesInitializer.java?rev=941020&r1=941019&r2=941020&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/webapp/AbstractFacesInitializer.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/webapp/AbstractFacesInitializer.java Tue May 4 20:06:31 2010
@@ -34,7 +34,9 @@ import javax.faces.context.FacesContext;
import javax.faces.context.FacesContextFactory;
import javax.faces.event.PostConstructApplicationEvent;
import javax.faces.event.PreDestroyApplicationEvent;
+import javax.faces.lifecycle.Lifecycle;
import javax.faces.lifecycle.LifecycleFactory;
+import javax.faces.webapp.FacesServlet;
import javax.servlet.ServletContext;
import org.apache.myfaces.application.ApplicationImpl;
@@ -48,6 +50,7 @@ import org.apache.myfaces.config.annotat
import org.apache.myfaces.context.servlet.ServletExternalContextImpl;
import org.apache.myfaces.shared_impl.util.StateUtils;
import org.apache.myfaces.shared_impl.webapp.webxml.WebXml;
+import org.apache.myfaces.view.facelets.tag.ui.DebugPhaseListener;
/**
* Performs common initialization tasks.
@@ -160,6 +163,15 @@ public abstract class AbstractFacesIniti
message.append("*** See Application#getProjectStage() for more information. ***\n");
message.append("*******************************************************************\n");
log.log(Level.WARNING, message.toString());
+
+ // if ProjectStage is Development, install the DebugPhaseListener
+ if (FacesContext.getCurrentInstance().isProjectStage(ProjectStage.Development))
+ {
+ LifecycleFactory lifeFac = (LifecycleFactory) FactoryFinder
+ .getFactory(FactoryFinder.LIFECYCLE_FACTORY);
+ Lifecycle lifecycle = lifeFac.getLifecycle(getLifecycleId(servletContext));
+ lifecycle.addPhaseListener(new DebugPhaseListener());
+ }
}
releaseFacesContext();
@@ -186,7 +198,7 @@ public abstract class AbstractFacesIniti
* @param eventClass the class to be passed down into the dispatching
* code
*/
- private void dispatchInitDestroyEvent(Object servletContext, Class eventClass) {
+ private void dispatchInitDestroyEvent(ServletContext servletContext, Class eventClass) {
ApplicationFactory appFac = (ApplicationFactory) FactoryFinder.getFactory(FactoryFinder.APPLICATION_FACTORY);
FacesContext fc = null;
@@ -194,7 +206,10 @@ public abstract class AbstractFacesIniti
if (fc == null) {
LifecycleFactory lifeFac = (LifecycleFactory) FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
FacesContextFactory facFac = (FacesContextFactory) FactoryFinder.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
- fc = facFac.getFacesContext(servletContext, new _SystemEventServletRequest(), new _SystemEventServletResponse(), lifeFac.getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE));
+ fc = facFac.getFacesContext(servletContext,
+ new _SystemEventServletRequest(),
+ new _SystemEventServletResponse(),
+ lifeFac.getLifecycle(getLifecycleId(servletContext)));
}
// in order to allow FacesContext.getViewRoot calls during startup/shutdown listeners,
@@ -205,6 +220,23 @@ public abstract class AbstractFacesIniti
appFac.getApplication().publishEvent(fc, eventClass, Application.class, appFac.getApplication());
}
+
+ /**
+ * Gets the LifecycleId from the ServletContext init param.
+ * If this is null, it returns LifecycleFactory.DEFAULT_LIFECYCLE.
+ * @param servletContext
+ * @return
+ */
+ private String getLifecycleId(ServletContext servletContext)
+ {
+ String id = servletContext.getInitParameter(FacesServlet.LIFECYCLE_ID_ATTR);
+
+ if (id != null)
+ {
+ return id;
+ }
+ return LifecycleFactory.DEFAULT_LIFECYCLE;
+ }
/**
* Cleans up all remaining resources (well, theoretically).
Modified: myfaces/core/trunk/impl/src/main/resources/META-INF/rsc/myfaces-dev-debug.xml
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/resources/META-INF/rsc/myfaces-dev-debug.xml?rev=941020&r1=941019&r2=941020&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/resources/META-INF/rsc/myfaces-dev-debug.xml (original)
+++ myfaces/core/trunk/impl/src/main/resources/META-INF/rsc/myfaces-dev-debug.xml Tue May 4 20:06:31 2010
@@ -5,17 +5,16 @@
<title>Debug - @@message@@</title>
<style type="text/css">
body, div, span, td, th, caption { font-family: 'Trebuchet MS', Verdana, Arial, Sans-Serif; font-size: small; }
-ul, li, pre { padding: 0; margin: 0; }
h1 { color: #090; }
h2, h2 span { font-size: large; color: #339; }
-h2 a { text-decoration: none; color: #339; }
+h2 a, .grayBox a { text-decoration: none; color: #339; }
.grayBox { padding: 8px; margin: 10px 0; border: 1px solid #CCC; background-color: #f9f9f9; }
#view { color: #090; font-weight: bold; font-size: medium; }
-#tree, #vars { display: none; }
+#tree, #extendedtree, #vars { display: none; }
code { font-size: medium; }
-#tree dl { color: #006; }
-#tree dd { margin-top: 2px; margin-bottom: 2px; }
-#tree dt { border: 1px solid #DDD; padding: 4px; border-left: 2px solid #666; font-family: "Courier New", Courier, mono; font-size: small; }
+#tree dl, #extendedtree dl { color: #006; }
+#tree dd, #extendedtree dd { margin-top: 2px; margin-bottom: 2px; }
+#tree dt, #extendedtree dt { border: 1px solid #DDD; padding: 4px; border-left: 2px solid #666; font-family: "Courier New", Courier, mono; font-size: small; }
.uicText { color: #999; }
.highlightComponent { color: #FFFFFF; background-color: #FF0000; }
table { border: 1px solid #CCC; border-collapse: collapse; border-spacing: 0px; width: 100%; text-align: left; }
@@ -23,6 +22,8 @@ td { border: 1px solid #CCC; }
thead tr th { padding: 2px; color: #030; background-color: #F9F9F9; }
tbody tr td { padding: 10px 6px; }
table caption { text-align: left; padding: 10px 0; font-size: large; }
+.lifecycle_values { display:none; margin: 5px 5px 5px 20px; }
+.lifecycle_values_wrapper, .renderer { margin: 5px 0px 5px 40px; }
</style>
<style type="text/css" media="print">
#trace, #tree, #vars { display: block; }
@@ -47,6 +48,8 @@ function toggle(id) {
<div id="error" class="grayBox" style="border: 1px solid #090;">@@message@@</div>
<h2><a href="#" onclick="toggle('tree'); return false;"><span id="treeOff">+</span><span id="treeOn" style="display: none;">-</span> Component Tree</a></h2>
<div id="tree" class="grayBox"><code>@@tree@@</code></div>
+<h2><a href="#" onclick="toggle('extendedtree'); return false;"><span id="extendedtreeOff">+</span><span id="extendedtreeOn" style="display: none;">-</span> Extended Component Tree</a></h2>
+<div id="extendedtree" class="grayBox"><code>@@extendedtree@@</code></div>
<h2><a href="#" onclick="toggle('vars'); return false;"><span id="varsOff">+</span><span id="varsOn" style="display: none;">-</span> Scoped Variables</a></h2>
<div id="vars">@@vars@@</div>
<div class="grayBox" style="text-align: right; color: #666;">@@now@@ - Generated by MyFaces - for information on disabling or modifying this error-page, see <a