com.jsftoolkit.base.HtmlScripts Maven / Gradle / Ivy
Go to download
The core classes for the JSF Toolkit Component Framework. Includes all
framework base and utility classes as well as component
kick-start/code-generation and registration tools.
Also includes some classes for testing that are reused in other projects.
They cannot be factored out into a separate project because they are
referenced by the tests and they reference this code (circular
dependence).
The newest version!
package com.jsftoolkit.base;
import java.io.IOException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import javax.faces.component.UIComponent;
import javax.faces.component.UIComponentBase;
import javax.faces.component.UIData;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.render.Renderer;
import com.jsftoolkit.base.renderer.ResourceUtils;
import com.jsftoolkit.utils.IOExceptionWrapper;
import com.jsftoolkit.utils.Utils;
/**
* Depth first searches the component tree for components or components with
* renderers that implement {@link HeadInsert}. The classpath resources are
* then included via shale remoting.
*
* TODO See about refactoring to a 1 script element 1 style element model. Allow
* named template areas that other templates can include themselves in.
*
* @author noah
*
*/
public class HtmlScripts extends UIComponentBase implements HeadInsert {
private static final String DELIMETER = ";";
private transient Set resourceIds = new HashSet();
@Override
public void decode(FacesContext context) {
// process the renderer's decode method (if there is one)
super.decode(context);
resourceIds = Utils
.asSet(Utils.split(context.getExternalContext()
.getRequestParameterMap().get(getClientId(context)),
DELIMETER));
}
/**
* Calls {@link #processChildren(UIComponent, FacesContext)} on the
* {@link UIViewRoot}.
*/
@Override
public void encodeChildren(FacesContext context) throws IOException {
Utils.notNull(context, "context");
if (!isRendered()) {
return;
}
processChildren(context.getViewRoot(), context, new HashSet(),
resourceIds);
}
@Override
public void encodeEnd(FacesContext context) throws IOException {
super.encodeEnd(context);
// write out our id and the already rendered resources
ResponseWriter writer = context.getResponseWriter();
writer.startElement("script", this);
writer.writeAttribute("type", "text/javascript", null);
writer.writeAttribute("id", getClientId(context), "id");
writer.writeText("JsfTk.HtmlScriptsId = '", null);
writer.writeText(getClientId(context), "id");
writer.writeText("';\nJsfTk.HtmlScriptsResourceIds = '", null);
writer.writeText(Utils.join(resourceIds.iterator(), DELIMETER), null);
writer.writeText("';", null);
writer.endElement("script");
}
/**
* Recursively process the children of the given component, linking
* resources for those rendered components that implement {@link HeadInsert}.
* Note that component is not processed in this invocation.
*
* @param component
* @param context
* @param rendered
* the ids of components that have already rendered (important
* for components inside of UIData that may not necessarily be
* rendered for every row, e.g. header and footer facets)
* @param resourceIds
* @throws IOException
*/
public static void processChildren(UIComponent component,
FacesContext context, final Set rendered,
final Set resourceIds) throws IOException {
JsfIterator data = null;
// determine if it's UIData or a JsfIterator
if (component instanceof UIData) {
data = new UIDataProcessor.UIDataWrapper((UIData) component);
} else if (component instanceof JsfIterator) {
data = (JsfIterator) component;
}
if (data == null) {
// for non-iterators, process normally
Iterator it = component.getFacetsAndChildren();
while (it.hasNext()) {
processChild(context, it.next(), rendered, resourceIds);
}
} else {
// need to run iteration
try {
UIDataProcessor.iterate(context, data, new UIDataProcessor() {
@Override
public void processChild(FacesContext context,
UIComponent child) {
process(context, child);
}
@Override
public void processFacet(FacesContext context,
UIComponent facet, String name) {
process(context, facet);
}
private void process(FacesContext context, UIComponent child) {
try {
HtmlScripts.processChild(context, child, rendered,
resourceIds);
} catch (IOException e) {
throw new IOExceptionWrapper(e);
}
}
});
} catch (IOExceptionWrapper e) {
throw e.getWrapped();
}
}
}
protected static void processChild(FacesContext context, UIComponent child,
Set rendered, Set resourceIds) throws IOException {
if (!child.isRendered() || !rendered.add(child.getClientId(context))) {
// skip components that are not rendered (which means their children
// cannot be rendered either) or that have already been rendered
// (which means their children have as well)
return;
}
// resolve the component's renderer, if it has one
Renderer renderer = getRenderer(child, context);
// determine if the component or it's renderer implements HeadInsert
HeadInsert c = null;
if (renderer == null) {
// component renders itself
if (child instanceof HeadInsert) {
c = (HeadInsert) child;
}
} else {
if (renderer instanceof HeadInsert) {
c = (HeadInsert) renderer;
}
}
if (c != null) {
// let it write into head
c.encodeHead(context, child, resourceIds);
}
// process the child's children
processChildren(child, context, rendered, resourceIds);
}
/**
*
* @param c
* @param context
* @return the renderer for the given component.
*/
protected static Renderer getRenderer(UIComponent c, FacesContext context) {
String family = c.getFamily();
String rendererType = c.getRendererType();
if (family != null && rendererType != null) {
return context.getRenderKit().getRenderer(family, rendererType);
}
return null;
}
/**
* No family, this component is self-rendering.
*/
@Override
public String getFamily() {
return null;
}
/**
* No renderer type, this component is self-rendering.
*/
@Override
public String getRendererType() {
return null;
}
@Override
public boolean getRendersChildren() {
return true;
}
public void encodeHead(FacesContext context, UIComponent component,
Set resourceIds) throws IOException {
ResourceUtils.writeIncludes(context, component, Utils
.asSet(ResourceConstants.JSFTK_JS), resourceIds);
}
}