/* | |
* 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.wicket.core.util.string; | |
import org.apache.wicket.Component; | |
import org.apache.wicket.util.string.AppendingStringBuffer; | |
/** | |
* | |
*/ | |
public class ComponentStrings | |
{ | |
/** | |
* Construct. | |
*/ | |
private ComponentStrings() | |
{ | |
} | |
/** | |
* Creates a location stacktrace string representation for the component for reference when the | |
* render check fails. This method filters out most of the unnecessary parts of the stack trace. | |
* The message of the <code>location</code> is used as a verb in the rendered string. Use | |
* "added", "constructed" or similar verbs as values. | |
* | |
* @param component | |
* the component that was constructed or added and failed to render | |
* @param location | |
* the location where the component was created or added in the java code. | |
* @return a string giving the line precise location where the component was added or created. | |
*/ | |
public static String toString(final Component component, final Throwable location) | |
{ | |
Class<?> componentClass = component.getClass(); | |
// try to find the component type, if it is an inner element, then get | |
// the parent component. | |
String componentType = componentClass.getName(); | |
if (componentType.indexOf('$') >= 0) | |
{ | |
componentType = componentClass.getSuperclass().getName(); | |
} | |
componentType = componentType.substring(componentType.lastIndexOf('.') + 1); | |
// create a user friendly message, using the location's message as a | |
// differentiator for the message (e.g. "component foo was ***added***" | |
// or "component foo was ***created***") | |
AppendingStringBuffer sb = new AppendingStringBuffer("The " + componentType.toLowerCase() + | |
" with id '" + component.getId() + "' that failed to render was " + | |
location.getMessage() + "\n"); | |
// a list of stacktrace elements that need to be skipped in the location | |
// stack trace | |
String[] skippedElements = new String[] { "org.apache.wicket.MarkupContainer", | |
"org.apache.wicket.Component", "org.apache.wicket.markup" }; | |
// a list of stack trace elements that stop the traversal of the stack | |
// trace | |
String[] breakingElements = new String[] { "org.apache.wicket.protocol.http.WicketServlet", | |
"org.apache.wicket.protocol.http.WicketFilter", "java.lang.reflect" }; | |
StackTraceElement[] trace = location.getStackTrace(); | |
for (int i = 0; i < trace.length; i++) | |
{ | |
String traceString = trace[i].toString(); | |
if (shouldSkip(traceString, skippedElements)) | |
{ | |
// don't print this line, is wicket internal | |
continue; | |
} | |
if (!(traceString.startsWith("sun.reflect.") && i > 1)) | |
{ | |
// filter out reflection API calls from the stack trace | |
if (!traceString.contains("java.lang.reflect")) | |
{ | |
sb.append(" at "); | |
sb.append(traceString); | |
sb.append("\n"); | |
} | |
if (shouldSkip(traceString, breakingElements)) | |
{ | |
break; | |
} | |
} | |
} | |
sb.append("\n"); | |
return sb.toString(); | |
} | |
private static boolean shouldSkip(String text, String[] filters) | |
{ | |
for (String filter : filters) | |
{ | |
if (text.contains(filter)) | |
{ | |
return true; | |
} | |
} | |
return false; | |
} | |
} |