I take it that t:message is the Tomahawk control.
Perhaps a custom JSF control would fit the bill - sometimes you can't manipulate the existing tags to get the exact output you want.
You can get the messages from the FacesContext. The control's renderer writes the HTML to the page:
package custom; /*imports omitted*/
public class CustomErrorRenderer extends Renderer {
@Override @SuppressWarnings("unchecked")
public void encodeEnd(FacesContext context,
UIComponent component) throws IOException {
ResponseWriter writer = context.getResponseWriter();
writer.startElement("div", component);
writer.writeAttribute("id", component.getClientId(context), "id");
writer.writeAttribute("style", "color: red", null);
writer.startElement("ul", null);
Iterator<String> clientIds = context.getClientIdsWithMessages();
while (clientIds.hasNext()) {
String clientId = clientIds.next();
Iterator<FacesMessage> messages = context.getMessages(clientId);
if (!messages.hasNext()) { continue; }
String javaScript = "var field = document.getElementById('"
+ clientId + "');" + "if(field == null) return false;"
+ "field.focus(); return false;";
writer.startElement("li", null);
writer.startElement("a", null);
writer.writeAttribute("onclick", javaScript, null);
writer.writeAttribute("href", "#", null);
while (messages.hasNext()) {
writer.writeText(messages.next().getSummary(), null);
}
writer.endElement("a");
writer.endElement("li");
}
writer.endElement("ul");
writer.endElement("div");
}
}
This renderer is defined in faces-config.xml:
<render-kit>
<description>add to base HTML_BASIC renderkit</description>
<display-name>HTML_BASIC</display-name>
<render-kit-id>HTML_BASIC</render-kit-id>
<renderer>
<display-name>CustomErrorRenderer</display-name>
<component-family>javax.faces.Output</component-family>
<renderer-type>custom.CustomErrorRenderer</renderer-type>
<renderer-class>custom.CustomErrorRenderer</renderer-class>
</renderer>
</render-kit>
Since the sample component doesn't require any special attributes or logic, it can be defined using the existing UIOutput component:
<component>
<display-name>CustomErrorMessages</display-name>
<component-type>custom.Errors</component-type>
<component-class>javax.faces.component.UIOutput</component-class>
</component>
To integrate the control with JSPs, you also need to provide a tag class:
package custom;
import javax.faces.webapp.UIComponentELTag;
public class CustomErrorTag extends UIComponentELTag {
@Override
public String getComponentType() {
return "custom.Errors";
}
@Override
public String getRendererType() {
return "custom.CustomErrorRenderer";
}
}
This is defined in a TLD file:
<?xml version="1.0" encoding="UTF-8"?>
<taglib xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
version="2.1">
<tlib-version>1.0</tlib-version>
<short-name>custom</short-name>
<uri>http://custom</uri>
<tag>
<name>errors</name>
<tag-class>custom.CustomErrorTag</tag-class>
<body-content>empty</body-content>
</tag>
</taglib>
If you wish to include this via a separate page, this sample JSP holds the control:
<%@ taglib prefix="custom" uri="http://custom"%>
<custom:errors />
This JSP includes it:
<%@taglib uri="http://java.sun.com/jsf/html" prefix="h"%><%@taglib
uri="http://java.sun.com/jsf/core" prefix="f"%>
<html>
<body>
<f:view>
<h:form>
<h:inputText>
<f:validateLength minimum="6" maximum="10" />
</h:inputText>
<h:inputText>
<f:validateLength minimum="4" maximum="6" />
</h:inputText>
<h:commandButton />
</h:form>
<f:subview id="errs">
<jsp:include page="customerrors2_inc.jsp" />
</f:subview>
</f:view>
</body>
</html>
Yep, it all seems too complicated, but at least you could use this to create a JAR with a reusable custom control that can be dropped into any JSF application.
You could probably achieve similar results using a managed bean and a repeating control, but then you have to start worrying about delaying the list creation until after the validation phase (which will probably involve a phase listener).