You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@struts.apache.org by dg...@apache.org on 2006/02/09 08:45:44 UTC
svn commit: r376215 - in /struts/shale/trunk:
clay-plugin/src/java/org/apache/shale/clay/component/
use-cases/src/java/org/apache/shale/usecases/symbols/
use-cases/src/web/ajax/ use-cases/src/web/symbols/ xdocs/ xdocs/images/
Author: dgeary
Date: Wed Feb 8 23:45:42 2006
New Revision: 376215
URL: http://svn.apache.org/viewcvs?rev=376215&view=rev
Log:
Added an introduction to Clay and a segment on HTML Views in addition to minor tweaks to Clay.java and the Ajax use case.
Added:
struts/shale/trunk/xdocs/images/login-clay-red.jpg (with props)
struts/shale/trunk/xdocs/images/login-clay.jpg (with props)
struts/shale/trunk/xdocs/images/login-html-red.jpg (with props)
struts/shale/trunk/xdocs/images/login-html.jpg (with props)
struts/shale/trunk/xdocs/images/login.jpg (with props)
Modified:
struts/shale/trunk/clay-plugin/src/java/org/apache/shale/clay/component/Clay.java
struts/shale/trunk/use-cases/src/java/org/apache/shale/usecases/symbols/RegistrationForm.java
struts/shale/trunk/use-cases/src/web/ajax/zipCode.jsp
struts/shale/trunk/use-cases/src/web/symbols/home.jsp
struts/shale/trunk/use-cases/src/web/symbols/nameReuse.jsp
struts/shale/trunk/xdocs/features-reusable-views.xml
struts/shale/trunk/xdocs/index.xml
struts/shale/trunk/xdocs/navigation.xml
Modified: struts/shale/trunk/clay-plugin/src/java/org/apache/shale/clay/component/Clay.java
URL: http://svn.apache.org/viewcvs/struts/shale/trunk/clay-plugin/src/java/org/apache/shale/clay/component/Clay.java?rev=376215&r1=376214&r2=376215&view=diff
==============================================================================
--- struts/shale/trunk/clay-plugin/src/java/org/apache/shale/clay/component/Clay.java (original)
+++ struts/shale/trunk/clay-plugin/src/java/org/apache/shale/clay/component/Clay.java Wed Feb 8 23:45:42 2006
@@ -45,6 +45,10 @@
import org.apache.shale.clay.config.beans.ConfigBeanFactory;
import org.apache.shale.util.Messages;
+/**
+ * This component grafts a subview onto the current JSF view.
+ *
+ */
public class Clay extends UINamingContainer {
/**
@@ -326,7 +330,7 @@
}
// Recursively build the subtree and fixup references. The root
- // of the subview will be assinged to the child private property.
+ // of the subview will be assigned to the child private property.
ClayContext clayContext = new ClayContext();
clayContext.setChild(null);
@@ -337,8 +341,7 @@
Map symbolTable = new TreeMap();
symbolTable.putAll(getDisplayElementRoot().getSymbols());
symbolTable.putAll(getSymbols());
- clayContext.setSymbols(symbolTable);
-
+ clayContext.setSymbols(symbolTable);
clayContext.setRootElement(getDisplayElementRoot());
clayContext.setParent(this);
@@ -393,7 +396,7 @@
/**
* <p>
- * Calls to super and adds some trace logging.
+ * Called by JSF, this method delegates to {@link recursiveRenderChildren}.
* </p>
*/
public void encodeChildren(FacesContext context) throws IOException {
@@ -407,8 +410,8 @@
/**
* <p>
- * Calls to super and invokes the rendering of the sub component tree if the
- * parent component doesn't render it's children.
+ * Called by JSF, this method simply emits a logging statement
+ * if tracing is enabled.
* </p>
*/
public void encodeEnd(FacesContext context) throws IOException {
@@ -416,7 +419,7 @@
if (log.isTraceEnabled())
log.trace("encodeEnd(FacesContext)");
- }
+ }
/**
* <p>
@@ -478,10 +481,8 @@
/**
* <p>Returns <code>true</code> indicating that this
- * component will render it's children. This will
- * indicate to a parent component that renders children
- * not to recursively render Clay's children -
- * simply invoke encodeChildren(FacesContext).</p>
+ * component renders it's children. That means JSF will
+ * invoke <code>encodeChildren()</code> method.
*/
public boolean getRendersChildren() {
return true;
Modified: struts/shale/trunk/use-cases/src/java/org/apache/shale/usecases/symbols/RegistrationForm.java
URL: http://svn.apache.org/viewcvs/struts/shale/trunk/use-cases/src/java/org/apache/shale/usecases/symbols/RegistrationForm.java?rev=376215&r1=376214&r2=376215&view=diff
==============================================================================
--- struts/shale/trunk/use-cases/src/java/org/apache/shale/usecases/symbols/RegistrationForm.java (original)
+++ struts/shale/trunk/use-cases/src/java/org/apache/shale/usecases/symbols/RegistrationForm.java Wed Feb 8 23:45:42 2006
@@ -30,10 +30,10 @@
public class RegistrationForm extends AbstractViewController {
/**
- * <p>This view controller is scoped in the request. The init method
- * is invoked each request and the page's state is handle using
- * session scoped backing beans. The dialog for these session scoped
- * backing beans are managed through this stateless controller.
+ * <p>This view controller is scoped in the request. The init method
+ * is invoked for each request and the page's state is stored in
+ * session scoped beans. The dialog for these session scoped
+ * beans are managed through this stateless controller.
* This separation of view model data and action controller is similar
* to Struts Action 1.x.</p>
*/
Modified: struts/shale/trunk/use-cases/src/web/ajax/zipCode.jsp
URL: http://svn.apache.org/viewcvs/struts/shale/trunk/use-cases/src/web/ajax/zipCode.jsp?rev=376215&r1=376214&r2=376215&view=diff
==============================================================================
--- struts/shale/trunk/use-cases/src/web/ajax/zipCode.jsp (original)
+++ struts/shale/trunk/use-cases/src/web/ajax/zipCode.jsp Wed Feb 8 23:45:42 2006
@@ -29,13 +29,13 @@
<!--
var req; // XMLHttpRequest
- // zipChanged(zip) is called when a selection is made
+ // zipChanged(zip) is called when a selection is made
// from the zip code menu.
function zipChanged(zip) {
- sendRequest("http://localhost:8080/struts-shale-usecases/" +
+ sendRequest(<%= request.getContextPath() %> +
"dynamic/remoting$business/cityAndStateForZip.faces" +
- "?zip=" + escape(zip),
+ "?zip=" + escape(zip),
processZipCodeSelection);
}
@@ -84,9 +84,9 @@
var stateElement = window.document
.getElementById("ajaxZipForm:state");
cityElement.value = city;
- stateElement.value = therest.substring(stateStartIndex,
+ stateElement.value = therest.substring(stateStartIndex,
stateEndIndex);
- }
+ }
}
}
}
Modified: struts/shale/trunk/use-cases/src/web/symbols/home.jsp
URL: http://svn.apache.org/viewcvs/struts/shale/trunk/use-cases/src/web/symbols/home.jsp?rev=376215&r1=376214&r2=376215&view=diff
==============================================================================
--- struts/shale/trunk/use-cases/src/web/symbols/home.jsp (original)
+++ struts/shale/trunk/use-cases/src/web/symbols/home.jsp Wed Feb 8 23:45:42 2006
@@ -50,7 +50,922 @@
<f:param name="url" value="/symbols/layout.html"/></h:outputLink> that defines
sections of a typical page having a header,
footer, left navbar and body sections. The layout is defined as an HTML template,
- but the entry point can be a full XML or full HTML template. There is a central
+ but the entry point can be a full XML or full HTML teIndex: src/java/org/apache/shale/faces/ValidatorRenderKit.java
+===================================================================
+--- src/java/org/apache/shale/faces/ValidatorRenderKit.java (revision 0)
++++ src/java/org/apache/shale/faces/ValidatorRenderKit.java (revision 0)
+@@ -0,0 +1,68 @@
++/*
++ * Copyright 2006 The Apache Software Foundation.
++ *
++ * Licensed 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.shale.faces;
++
++import java.io.OutputStream;
++import java.io.Writer;
++
++import javax.faces.context.ResponseStream;
++import javax.faces.context.ResponseWriter;
++import javax.faces.render.RenderKit;
++import javax.faces.render.Renderer;
++import javax.faces.render.ResponseStateManager;
++
++import org.apache.shale.renderer.ValidatorCommandRenderer;
++
++public class ValidatorRenderKit extends RenderKit {
++
++ private RenderKit defaultRenderKit = null;
++ public ValidatorRenderKit(RenderKit defaultRenderKit) {
++ this.defaultRenderKit = defaultRenderKit;
++ }
++
++ public void addRenderer(String componentFamily, String rendererType, Renderer renderer) {
++ this.defaultRenderKit.addRenderer(componentFamily, rendererType, renderer);
++ }
++
++ public Renderer getRenderer(String componentFamily, String rendererType) {
++ Renderer target = defaultRenderKit.getRenderer(componentFamily, rendererType);
++ if (componentFamily.equals("javax.faces.Command")) {
++ if (!(target instanceof ValidatorCommandRenderer)) {
++ target = new ValidatorCommandRenderer(target);
++ addRenderer(componentFamily, rendererType, target);
++ }
++ }
++
++ return target;
++ }
++
++ public ResponseStateManager getResponseStateManager() {
++ return defaultRenderKit.getResponseStateManager();
++ }
++
++ public ResponseWriter createResponseWriter(Writer writer,
++ String contentTypeList,
++ String characterEncoding){
++ return defaultRenderKit.createResponseWriter(writer, contentTypeList, characterEncoding);
++ }
++
++ public ResponseStream createResponseStream(OutputStream outputStream) {
++ return defaultRenderKit.createResponseStream(outputStream);
++ }
++
++}
+Index: src/java/org/apache/shale/validator/Globals.java
+===================================================================
+--- src/java/org/apache/shale/validator/Globals.java (revision 367478)
++++ src/java/org/apache/shale/validator/Globals.java (working copy)
+@@ -19,7 +19,7 @@
+ /**
+ * <p>Manifest constants that are global to the validator implementation.</p>
+ *
+- * $Id:$
++ * $Id$
+ */
+
+ public class Globals {
+Index: src/java/org/apache/shale/renderer/ValidatorCommandRenderer.java
+===================================================================
+--- src/java/org/apache/shale/renderer/ValidatorCommandRenderer.java (revision 0)
++++ src/java/org/apache/shale/renderer/ValidatorCommandRenderer.java (revision 0)
+@@ -0,0 +1,168 @@
++/*
++ * Copyright 2006 The Apache Software Foundation.
++ *
++ * Licensed 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.shale.renderer;
++
++import java.io.IOException;
++import java.io.StringWriter;
++
++import javax.faces.component.UICommand;
++import javax.faces.component.UIComponent;
++import javax.faces.context.FacesContext;
++import javax.faces.context.ResponseWriter;
++import javax.faces.render.Renderer;
++
++public class ValidatorCommandRenderer extends Renderer {
++
++ private Renderer defaultRenderer = null;
++ public ValidatorCommandRenderer(Renderer defaultRenderer) {
++ this.defaultRenderer = defaultRenderer;
++ }
++
++
++
++ /**
++ * <p>
++ * Attribute name used to override the default behavior of how the immediate
++ * attribute effects the execution of client side javascript validation.
++ * </p>
++ */
++ public static final String OVERRIDE_IMMEDIATE = "org.apache.shale.validator.immediate";
++
++ private static final int ENCODE_BEGIN = 0;
++
++ private static final int ENCODE_CHILDREN = 1;
++
++ private static final int ENCODE_END = 2;
++
++ /**
++ * <b>Interrogates the component's immediate property and the component's
++ * immediate override attribute to determine if client side validation is
++ * invoked. If either the property or attribute override is false, client
++ * side validation is invoked. Otherwise, the response writer is hijacked
++ * and the original render is invoked. The result is buffered and a
++ * statement of javascript is injected into the onclick event which cancels
++ * client side validation. The original response writer is restored and the
++ * modified markup is written to the response writer. The
++ * <code>encodeSwitch</code> determines if the encodeBegin, encodeChildren
++ * or encodeEnd methods should be invoked on the decorated renderer.</b>
++ */
++ protected void encode(FacesContext context, UIComponent component,
++ int encodeSwitch) throws IOException {
++
++ UICommand command = (UICommand) component;
++
++ // look for a override to the default
++ boolean immediateOverride = true;
++ String attr = (String) component.getAttributes()
++ .get(OVERRIDE_IMMEDIATE);
++ if (attr != null) {
++ immediateOverride = Boolean.valueOf(attr).booleanValue();
++ }
++
++ if (command.isImmediate() && immediateOverride) {
++
++ ResponseWriter hijackedWriter = context.getResponseWriter();
++ // builds a buffer to write the page to
++ StringWriter writer = new StringWriter();
++ // create a buffered response writer
++ ResponseWriter buffResponsewriter = context.getRenderKit()
++ .createResponseWriter(writer, null,
++ hijackedWriter.getCharacterEncoding());
++ // push buffered writer to the faces context
++ context.setResponseWriter(buffResponsewriter);
++
++ if (encodeSwitch == ENCODE_BEGIN)
++ defaultRenderer.encodeBegin(context, component);
++ else if (encodeSwitch == ENCODE_CHILDREN)
++ defaultRenderer.encodeChildren(context, component);
++ else
++ defaultRenderer.encodeEnd(context, component);
++
++ StringBuffer buff = writer.getBuffer();
++ int i = buff.indexOf("onclick=\"");
++ if (i > 0) {
++ buff.insert(i + "onclick=\"".length(), "bCancel=true;");
++ }
++
++ hijackedWriter.write(buff.toString());
++ context.setResponseWriter(hijackedWriter);
++
++ } else {
++ if (encodeSwitch == ENCODE_BEGIN)
++ defaultRenderer.encodeBegin(context, component);
++ else if (encodeSwitch == ENCODE_CHILDREN)
++ defaultRenderer.encodeChildren(context, component);
++ else
++ defaultRenderer.encodeEnd(context, component);
++ }
++ }
++
++
++
++ /**
++ * <p>
++ * Decorates the original component renderer invoking its decode method.
++ * <p>
++ */
++ public void decode(FacesContext context, UIComponent component) {
++ defaultRenderer.decode(context, component);
++ }
++
++ /**
++ * <p>
++ * Invokes the <code>encode</code> method passing
++ * <code>ENCODE_BEGIN</code> for the encodeSwitch parameter.
++ * </p>
++ */
++ public void encodeBegin(FacesContext context, UIComponent component)
++ throws IOException {
++ encode(context, component, ENCODE_BEGIN);
++ }
++
++ /**
++ * <p>
++ * Invokes the <code>encode</code> method passing
++ * <code>ENCODE_CHILDREN</code> for the encodeSwitch parameter.
++ * </p>
++ */
++ public void encodeChildren(FacesContext context, UIComponent component)
++ throws IOException {
++ encode(context, component, ENCODE_CHILDREN);
++ }
++
++ /**
++ * <p>
++ * Invokes the <code>encode</code> method passing <code>ENCODE_END</code>
++ * for the encodeSwitch parameter.
++ * </p>
++ */
++ public void encodeEnd(FacesContext context, UIComponent component)
++ throws IOException {
++ encode(context, component, ENCODE_END);
++ }
++
++ /**
++ * <p>
++ * This method is hard-coded to return <code>true</code>. The other
++ * overriden methods invoke the decorated original render.
++ */
++ public boolean getRendersChildren() {
++ return defaultRenderer.getRendersChildren();
++ }
++
++}
+Index: src/java/org/apache/shale/view/faces/ViewViewHandler.java
+===================================================================
+--- src/java/org/apache/shale/view/faces/ViewViewHandler.java (revision 367478)
++++ src/java/org/apache/shale/view/faces/ViewViewHandler.java (working copy)
+@@ -23,15 +23,18 @@
+ import java.util.Map;
+
+ import javax.faces.FacesException;
++import javax.faces.FactoryFinder;
+ import javax.faces.application.ViewHandler;
+ import javax.faces.component.UIViewRoot;
+ import javax.faces.context.FacesContext;
+ import javax.faces.el.EvaluationException;
+-import javax.faces.el.ValueBinding;
+ import javax.faces.el.VariableResolver;
++import javax.faces.render.RenderKit;
++import javax.faces.render.RenderKitFactory;
+
+ import org.apache.commons.logging.Log;
+ import org.apache.commons.logging.LogFactory;
++import org.apache.shale.faces.ValidatorRenderKit;
+ import org.apache.shale.util.Messages;
+ import org.apache.shale.view.Constants;
+ import org.apache.shale.view.ViewController;
+@@ -124,6 +127,7 @@
+ public UIViewRoot createView(FacesContext context, String viewId) {
+ UIViewRoot view = original.createView(context, viewId);
+ setupViewController(context, view, viewId, false);
++ setupRenderKit(context, view);
+ return view;
+ }
+
+@@ -263,5 +267,13 @@
+
+ }
+
++
++ private void setupRenderKit(FacesContext context, UIViewRoot view) {
++ RenderKitFactory factory = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
++ RenderKit defaultRenderKit = factory.getRenderKit(context, view.getRenderKitId());
++ if (!(defaultRenderKit instanceof ValidatorRenderKit))
++ factory.addRenderKit(view.getRenderKitId(), new ValidatorRenderKit(defaultRenderKit));
++ }
+
++
+ }
+Index: src/java/org/apache/shale/faces/ValidatorRenderKit.java
+===================================================================
+--- src/java/org/apache/shale/faces/ValidatorRenderKit.java (revision 0)
++++ src/java/org/apache/shale/faces/ValidatorRenderKit.java (revision 0)
+@@ -0,0 +1,68 @@
++/*
++ * Copyright 2006 The Apache Software Foundation.
++ *
++ * Licensed 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.shale.faces;
++
++import java.io.OutputStream;
++import java.io.Writer;
++
++import javax.faces.context.ResponseStream;
++import javax.faces.context.ResponseWriter;
++import javax.faces.render.RenderKit;
++import javax.faces.render.Renderer;
++import javax.faces.render.ResponseStateManager;
++
++import org.apache.shale.renderer.ValidatorCommandRenderer;
++
++public class ValidatorRenderKit extends RenderKit {
++
++ private RenderKit defaultRenderKit = null;
++ public ValidatorRenderKit(RenderKit defaultRenderKit) {
++ this.defaultRenderKit = defaultRenderKit;
++ }
++
++ public void addRenderer(String componentFamily, String rendererType, Renderer renderer) {
++ this.defaultRenderKit.addRenderer(componentFamily, rendererType, renderer);
++ }
++
++ public Renderer getRenderer(String componentFamily, String rendererType) {
++ Renderer target = defaultRenderKit.getRenderer(componentFamily, rendererType);
++ if (componentFamily.equals("javax.faces.Command")) {
++ if (!(target instanceof ValidatorCommandRenderer)) {
++ target = new ValidatorCommandRenderer(target);
++ addRenderer(componentFamily, rendererType, target);
++ }
++ }
++
++ return target;
++ }
++
++ public ResponseStateManager getResponseStateManager() {
++ return defaultRenderKit.getResponseStateManager();
++ }
++
++ public ResponseWriter createResponseWriter(Writer writer,
++ String contentTypeList,
++ String characterEncoding){
++ return defaultRenderKit.createResponseWriter(writer, contentTypeList, characterEncoding);
++ }
++
++ public ResponseStream createResponseStream(OutputStream outputStream) {
++ return defaultRenderKit.createResponseStream(outputStream);
++ }
++
++}
+Index: src/java/org/apache/shale/validator/Globals.java
+===================================================================
+--- src/java/org/apache/shale/validator/Globals.java (revision 367478)
++++ src/java/org/apache/shale/validator/Globals.java (working copy)
+@@ -19,7 +19,7 @@
+ /**
+ * <p>Manifest constants that are global to the validator implementation.</p>
+ *
+- * $Id:$
++ * $Id$
+ */
+
+ public class Globals {
+Index: src/java/org/apache/shale/renderer/ValidatorCommandRenderer.java
+===================================================================
+--- src/java/org/apache/shale/renderer/ValidatorCommandRenderer.java (revision 0)
++++ src/java/org/apache/shale/renderer/ValidatorCommandRenderer.java (revision 0)
+@@ -0,0 +1,168 @@
++/*
++ * Copyright 2006 The Apache Software Foundation.
++ *
++ * Licensed 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.shale.renderer;
++
++import java.io.IOException;
++import java.io.StringWriter;
++
++import javax.faces.component.UICommand;
++import javax.faces.component.UIComponent;
++import javax.faces.context.FacesContext;
++import javax.faces.context.ResponseWriter;
++import javax.faces.render.Renderer;
++
++public class ValidatorCommandRenderer extends Renderer {
++
++ private Renderer defaultRenderer = null;
++ public ValidatorCommandRenderer(Renderer defaultRenderer) {
++ this.defaultRenderer = defaultRenderer;
++ }
++
++
++
++ /**
++ * <p>
++ * Attribute name used to override the default behavior of how the immediate
++ * attribute effects the execution of client side javascript validation.
++ * </p>
++ */
++ public static final String OVERRIDE_IMMEDIATE = "org.apache.shale.validator.immediate";
++
++ private static final int ENCODE_BEGIN = 0;
++
++ private static final int ENCODE_CHILDREN = 1;
++
++ private static final int ENCODE_END = 2;
++
++ /**
++ * <b>Interrogates the component's immediate property and the component's
++ * immediate override attribute to determine if client side validation is
++ * invoked. If either the property or attribute override is false, client
++ * side validation is invoked. Otherwise, the response writer is hijacked
++ * and the original render is invoked. The result is buffered and a
++ * statement of javascript is injected into the onclick event which cancels
++ * client side validation. The original response writer is restored and the
++ * modified markup is written to the response writer. The
++ * <code>encodeSwitch</code> determines if the encodeBegin, encodeChildren
++ * or encodeEnd methods should be invoked on the decorated renderer.</b>
++ */
++ protected void encode(FacesContext context, UIComponent component,
++ int encodeSwitch) throws IOException {
++
++ UICommand command = (UICommand) component;
++
++ // look for a override to the default
++ boolean immediateOverride = true;
++ String attr = (String) component.getAttributes()
++ .get(OVERRIDE_IMMEDIATE);
++ if (attr != null) {
++ immediateOverride = Boolean.valueOf(attr).booleanValue();
++ }
++
++ if (command.isImmediate() && immediateOverride) {
++
++ ResponseWriter hijackedWriter = context.getResponseWriter();
++ // builds a buffer to write the page to
++ StringWriter writer = new StringWriter();
++ // create a buffered response writer
++ ResponseWriter buffResponsewriter = context.getRenderKit()
++ .createResponseWriter(writer, null,
++ hijackedWriter.getCharacterEncoding());
++ // push buffered writer to the faces context
++ context.setResponseWriter(buffResponsewriter);
++
++ if (encodeSwitch == ENCODE_BEGIN)
++ defaultRenderer.encodeBegin(context, component);
++ else if (encodeSwitch == ENCODE_CHILDREN)
++ defaultRenderer.encodeChildren(context, component);
++ else
++ defaultRenderer.encodeEnd(context, component);
++
++ StringBuffer buff = writer.getBuffer();
++ int i = buff.indexOf("onclick=\"");
++ if (i > 0) {
++ buff.insert(i + "onclick=\"".length(), "bCancel=true;");
++ }
++
++ hijackedWriter.write(buff.toString());
++ context.setResponseWriter(hijackedWriter);
++
++ } else {
++ if (encodeSwitch == ENCODE_BEGIN)
++ defaultRenderer.encodeBegin(context, component);
++ else if (encodeSwitch == ENCODE_CHILDREN)
++ defaultRenderer.encodeChildren(context, component);
++ else
++ defaultRenderer.encodeEnd(context, component);
++ }
++ }
++
++
++
++ /**
++ * <p>
++ * Decorates the original component renderer invoking its decode method.
++ * <p>
++ */
++ public void decode(FacesContext context, UIComponent component) {
++ defaultRenderer.decode(context, component);
++ }
++
++ /**
++ * <p>
++ * Invokes the <code>encode</code> method passing
++ * <code>ENCODE_BEGIN</code> for the encodeSwitch parameter.
++ * </p>
++ */
++ public void encodeBegin(FacesContext context, UIComponent component)
++ throws IOException {
++ encode(context, component, ENCODE_BEGIN);
++ }
++
++ /**
++ * <p>
++ * Invokes the <code>encode</code> method passing
++ * <code>ENCODE_CHILDREN</code> for the encodeSwitch parameter.
++ * </p>
++ */
++ public void encodeChildren(FacesContext context, UIComponent component)
++ throws IOException {
++ encode(context, component, ENCODE_CHILDREN);
++ }
++
++ /**
++ * <p>
++ * Invokes the <code>encode</code> method passing <code>ENCODE_END</code>
++ * for the encodeSwitch parameter.
++ * </p>
++ */
++ public void encodeEnd(FacesContext context, UIComponent component)
++ throws IOException {
++ encode(context, component, ENCODE_END);
++ }
++
++ /**
++ * <p>
++ * This method is hard-coded to return <code>true</code>. The other
++ * overriden methods invoke the decorated original render.
++ */
++ public boolean getRendersChildren() {
++ return defaultRenderer.getRendersChildren();
++ }
++
++}
+Index: src/java/org/apache/shale/view/faces/ViewViewHandler.java
+===================================================================
+--- src/java/org/apache/shale/view/faces/ViewViewHandler.java (revision 367478)
++++ src/java/org/apache/shale/view/faces/ViewViewHandler.java (working copy)
+@@ -23,15 +23,18 @@
+ import java.util.Map;
+
+ import javax.faces.FacesException;
++import javax.faces.FactoryFinder;
+ import javax.faces.application.ViewHandler;
+ import javax.faces.component.UIViewRoot;
+ import javax.faces.context.FacesContext;
+ import javax.faces.el.EvaluationException;
+-import javax.faces.el.ValueBinding;
+ import javax.faces.el.VariableResolver;
++import javax.faces.render.RenderKit;
++import javax.faces.render.RenderKitFactory;
+
+ import org.apache.commons.logging.Log;
+ import org.apache.commons.logging.LogFactory;
++import org.apache.shale.faces.ValidatorRenderKit;
+ import org.apache.shale.util.Messages;
+ import org.apache.shale.view.Constants;
+ import org.apache.shale.view.ViewController;
+@@ -124,6 +127,7 @@
+ public UIViewRoot createView(FacesContext context, String viewId) {
+ UIViewRoot view = original.createView(context, viewId);
+ setupViewController(context, view, viewId, false);
++ setupRenderKit(context, view);
+ return view;
+ }
+
+@@ -263,5 +267,13 @@
+
+ }
+
++
++ private void setupRenderKit(FacesContext context, UIViewRoot view) {
++ RenderKitFactory factory = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
++ RenderKit defaultRenderKit = factory.getRenderKit(context, view.getRenderKitId());
++ if (!(defaultRenderKit instanceof ValidatorRenderKit))
++ factory.addRenderKit(view.getRenderKitId(), new ValidatorRenderKit(defaultRenderKit));
++ }
+
++
+ }
+Index: src/java/org/apache/shale/faces/ValidatorRenderKit.java
+===================================================================
+--- src/java/org/apache/shale/faces/ValidatorRenderKit.java (revision 0)
++++ src/java/org/apache/shale/faces/ValidatorRenderKit.java (revision 0)
+@@ -0,0 +1,68 @@
++/*
++ * Copyright 2006 The Apache Software Foundation.
++ *
++ * Licensed 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.shale.faces;
++
++import java.io.OutputStream;
++import java.io.Writer;
++
++import javax.faces.context.ResponseStream;
++import javax.faces.context.ResponseWriter;
++import javax.faces.render.RenderKit;
++import javax.faces.render.Renderer;
++import javax.faces.render.ResponseStateManager;
++
++import org.apache.shale.renderer.ValidatorCommandRenderer;
++
++public class ValidatorRenderKit extends RenderKit {
++
++ private RenderKit defaultRenderKit = null;
++ public ValidatorRenderKit(RenderKit defaultRenderKit) {
++ this.defaultRenderKit = defaultRenderKit;
++ }
++
++ public void addRenderer(String componentFamily, String rendererType, Renderer renderer) {
++ this.defaultRenderKit.addRenderer(componentFamily, rendererType, renderer);
++ }
++
++ public Renderer getRenderer(String componentFamily, String rendererType) {
++ Renderer target = defaultRenderKit.getRenderer(componentFamily, rendererType);
++ if (componentFamily.equals("javax.faces.Command")) {
++ if (!(target instanceof ValidatorCommandRenderer)) {
++ target = new ValidatorCommandRenderer(target);
++ addRenderer(componentFamily, rendererType, target);
++ }
++ }
++
++ return target;
++ }
++
++ public ResponseStateManager getResponseStateManager() {
++ return defaultRenderKit.getResponseStateManager();
++ }
++
++ public ResponseWriter createResponseWriter(Writer writer,
++ String contentTypeList,
++ String characterEncoding){
++ return defaultRenderKit.createResponseWriter(writer, contentTypeList, characterEncoding);
++ }
++
++ public ResponseStream createResponseStream(OutputStream outputStream) {
++ return defaultRenderKit.createResponseStream(outputStream);
++ }
++
++}
+Index: src/java/org/apache/shale/validator/Globals.java
+===================================================================
+--- src/java/org/apache/shale/validator/Globals.java (revision 367478)
++++ src/java/org/apache/shale/validator/Globals.java (working copy)
+@@ -19,7 +19,7 @@
+ /**
+ * <p>Manifest constants that are global to the validator implementation.</p>
+ *
+- * $Id:$
++ * $Id$
+ */
+
+ public class Globals {
+Index: src/java/org/apache/shale/renderer/ValidatorCommandRenderer.java
+===================================================================
+--- src/java/org/apache/shale/renderer/ValidatorCommandRenderer.java (revision 0)
++++ src/java/org/apache/shale/renderer/ValidatorCommandRenderer.java (revision 0)
+@@ -0,0 +1,168 @@
++/*
++ * Copyright 2006 The Apache Software Foundation.
++ *
++ * Licensed 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.shale.renderer;
++
++import java.io.IOException;
++import java.io.StringWriter;
++
++import javax.faces.component.UICommand;
++import javax.faces.component.UIComponent;
++import javax.faces.context.FacesContext;
++import javax.faces.context.ResponseWriter;
++import javax.faces.render.Renderer;
++
++public class ValidatorCommandRenderer extends Renderer {
++
++ private Renderer defaultRenderer = null;
++ public ValidatorCommandRenderer(Renderer defaultRenderer) {
++ this.defaultRenderer = defaultRenderer;
++ }
++
++
++
++ /**
++ * <p>
++ * Attribute name used to override the default behavior of how the immediate
++ * attribute effects the execution of client side javascript validation.
++ * </p>
++ */
++ public static final String OVERRIDE_IMMEDIATE = "org.apache.shale.validator.immediate";
++
++ private static final int ENCODE_BEGIN = 0;
++
++ private static final int ENCODE_CHILDREN = 1;
++
++ private static final int ENCODE_END = 2;
++
++ /**
++ * <b>Interrogates the component's immediate property and the component's
++ * immediate override attribute to determine if client side validation is
++ * invoked. If either the property or attribute override is false, client
++ * side validation is invoked. Otherwise, the response writer is hijacked
++ * and the original render is invoked. The result is buffered and a
++ * statement of javascript is injected into the onclick event which cancels
++ * client side validation. The original response writer is restored and the
++ * modified markup is written to the response writer. The
++ * <code>encodeSwitch</code> determines if the encodeBegin, encodeChildren
++ * or encodeEnd methods should be invoked on the decorated renderer.</b>
++ */
++ protected void encode(FacesContext context, UIComponent component,
++ int encodeSwitch) throws IOException {
++
++ UICommand command = (UICommand) component;
++
++ // look for a override to the default
++ boolean immediateOverride = true;
++ String attr = (String) component.getAttributes()
++ .get(OVERRIDE_IMMEDIATE);
++ if (attr != null) {
++ immediateOverride = Boolean.valueOf(attr).booleanValue();
++ }
++
++ if (command.isImmediate() && immediateOverride) {
++
++ ResponseWriter hijackedWriter = context.getResponseWriter();
++ // builds a buffer to write the page to
++ StringWriter writer = new StringWriter();
++ // create a buffered response writer
++ ResponseWriter buffResponsewriter = context.getRenderKit()
++ .createResponseWriter(writer, null,
++ hijackedWriter.getCharacterEncoding());
++ // push buffered writer to the faces context
++ context.setResponseWriter(buffResponsewriter);
++
++ if (encodeSwitch == ENCODE_BEGIN)
++ defaultRenderer.encodeBegin(context, component);
++ else if (encodeSwitch == ENCODE_CHILDREN)
++ defaultRenderer.encodeChildren(context, component);
++ else
++ defaultRenderer.encodeEnd(context, component);
++
++ StringBuffer buff = writer.getBuffer();
++ int i = buff.indexOf("onclick=\"");
++ if (i > 0) {
++ buff.insert(i + "onclick=\"".length(), "bCancel=true;");
++ }
++
++ hijackedWriter.write(buff.toString());
++ context.setResponseWriter(hijackedWriter);
++
++ } else {
++ if (encodeSwitch == ENCODE_BEGIN)
++ defaultRenderer.encodeBegin(context, component);
++ else if (encodeSwitch == ENCODE_CHILDREN)
++ defaultRenderer.encodeChildren(context, component);
++ else
++ defaultRenderer.encodeEnd(context, component);
++ }
++ }
++
++
++
++ /**
++ * <p>
++ * Decorates the original component renderer invoking its decode method.
++ * <p>
++ */
++ public void decode(FacesContext context, UIComponent component) {
++ defaultRenderer.decode(context, component);
++ }
++
++ /**
++ * <p>
++ * Invokes the <code>encode</code> method passing
++ * <code>ENCODE_BEGIN</code> for the encodeSwitch parameter.
++ * </p>
++ */
++ public void encodeBegin(FacesContext context, UIComponent component)
++ throws IOException {
++ encode(context, component, ENCODE_BEGIN);
++ }
++
++ /**
++ * <p>
++ * Invokes the <code>encode</code> method passing
++ * <code>ENCODE_CHILDREN</code> for the encodeSwitch parameter.
++ * </p>
++ */
++ public void encodeChildren(FacesContext context, UIComponent component)
++ throws IOException {
++ encode(context, component, ENCODE_CHILDREN);
++ }
++
++ /**
++ * <p>
++ * Invokes the <code>encode</code> method passing <code>ENCODE_END</code>
++ * for the encodeSwitch parameter.
++ * </p>
++ */
++ public void encodeEnd(FacesContext context, UIComponent component)
++ throws IOException {
++ encode(context, component, ENCODE_END);
++ }
++
++ /**
++ * <p>
++ * This method is hard-coded to return <code>true</code>. The other
++ * overriden methods invoke the decorated original render.
++ */
++ public boolean getRendersChildren() {
++ return defaultRenderer.getRendersChildren();
++ }
++
++}
+Index: src/java/org/apache/shale/view/faces/ViewViewHandler.java
+===================================================================
+--- src/java/org/apache/shale/view/faces/ViewViewHandler.java (revision 367478)
++++ src/java/org/apache/shale/view/faces/ViewViewHandler.java (working copy)
+@@ -23,15 +23,18 @@
+ import java.util.Map;
+
+ import javax.faces.FacesException;
++import javax.faces.FactoryFinder;
+ import javax.faces.application.ViewHandler;
+ import javax.faces.component.UIViewRoot;
+ import javax.faces.context.FacesContext;
+ import javax.faces.el.EvaluationException;
+-import javax.faces.el.ValueBinding;
+ import javax.faces.el.VariableResolver;
++import javax.faces.render.RenderKit;
++import javax.faces.render.RenderKitFactory;
+
+ import org.apache.commons.logging.Log;
+ import org.apache.commons.logging.LogFactory;
++import org.apache.shale.faces.ValidatorRenderKit;
+ import org.apache.shale.util.Messages;
+ import org.apache.shale.view.Constants;
+ import org.apache.shale.view.ViewController;
+@@ -124,6 +127,7 @@
+ public UIViewRoot createView(FacesContext context, String viewId) {
+ UIViewRoot view = original.createView(context, viewId);
+ setupViewController(context, view, viewId, false);
++ setupRenderKit(context, view);
+ return view;
+ }
+
+@@ -263,5 +267,13 @@
+
+ }
+
++
++ private void setupRenderKit(FacesContext context, UIViewRoot view) {
++ RenderKitFactory factory = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
++ RenderKit defaultRenderKit = factory.getRenderKit(context, view.getRenderKitId());
++ if (!(defaultRenderKit instanceof ValidatorRenderKit))
++ factory.addRenderKit(view.getRenderKitId(), new ValidatorRenderKit(defaultRenderKit));
++ }
+
++
+ }
+mplate. There is a central
Clay configuration that is dedicated to full XML views and a base component definition
in the common clay configuration file.
<br/><br/>
Modified: struts/shale/trunk/use-cases/src/web/symbols/nameReuse.jsp
URL: http://svn.apache.org/viewcvs/struts/shale/trunk/use-cases/src/web/symbols/nameReuse.jsp?rev=376215&r1=376214&r2=376215&view=diff
==============================================================================
--- struts/shale/trunk/use-cases/src/web/symbols/nameReuse.jsp (original)
+++ struts/shale/trunk/use-cases/src/web/symbols/nameReuse.jsp Wed Feb 8 23:45:42 2006
@@ -43,7 +43,7 @@
<hr>
<clay:clay id="businessPerson" jsfid="businessPersonNamePanel" managedBeanName="businessPerson"/>
<br>
- <h:commandButton action="#{symbols$nameReuse.save}" value="Save"/> <h:commandButton action="home" value="Home" immediate="true"/>
+ <h:commandButton action="#{symbols$nameReuse.save}" value="Save"/><h:commandButton action="home" value="Home" immediate="true"/>
</h:form>
</body>
Modified: struts/shale/trunk/xdocs/features-reusable-views.xml
URL: http://svn.apache.org/viewcvs/struts/shale/trunk/xdocs/features-reusable-views.xml?rev=376215&r1=376214&r2=376215&view=diff
==============================================================================
--- struts/shale/trunk/xdocs/features-reusable-views.xml (original)
+++ struts/shale/trunk/xdocs/features-reusable-views.xml Wed Feb 8 23:45:42 2006
@@ -2,25 +2,290 @@
<document>
<properties>
- <title>Shale Reusable Views</title>
+ <title>Shale Clay</title>
</properties>
<body>
- <section name="Shale Reusable Views">
+ <section name="Clay">
<a name="clay"/>
<a name="clay-introduction"/>
- <subsection name="Clay Introduction">
+ <subsection name="Introduction">
- <p>The <strong>Clay</strong> component is a complex JavaServer Faces
- component that features a way to build more reusable view fragments.
- As its name implies, the Clay component can be shaped and molded into
- a subtree of JSF components acting as a "stand-in"
- for the JSP tags normally required to add components
- to the view's component tree.</p>
+ <p>Every JavaServer Faces (JSF) view is represented on the server as a
+ tree of components. Shale's <strong>Clay</strong> lets you graft a
+ component subtree onto an existing component tree.</p>
+ <p>At first blush, this may not seem like much; after all, you
+ can accomplish the same thing with JSF's standard <code>f:subview</code>
+ tag. However, Clay builds upon this fundamental feature to give you
+ the following killer capabilities:</p>
+ <ul>
+ <li><i>HTML Views</i></li>
+ <p>
+ Similar to <a href="">Tapestry</a> and
+ <a href="https://facelets.dev.java.net">Facelets</a>, you define your
+ views in HTML. For dynamic content, you tie HTML elements to JSF components
+ with a <code>jsfid</code> attribute--when Clay renders your view, it
+ replaces static HTML elements that have <code>jsfid</code> attributes with their
+ component counterpart. When Clay creates the components, it transmits HTML style
+ attributes from the HTML tag to the component; therefore, graphic designers can implement
+ a look and feel with mockup HTML and that look and feel is absorbed by the component
+ that replaces the mockup HTML.
+ </p>
+
+ <li><i>Meta-data Inheritance and Tiles-like Composition</i></li>
+ <p>
+ You can define a component as an extension of an existing component and
+ then override or add attributes. This mechamism is similar to the inheritance
+ mechanism built into the popular <a href="">Tiles</a> framework for composing
+ web pages from discrete JSP fragments, known as tiles. In fact, Clay's features
+ are sophisticated enough that Clay can give you much of the same benefits that
+ Tiles users enjoy.
+ </p>
+
+ <li><i>Symbols</i></li>
+ <p>
+ You can map managed beans to symbols, which you can use as though they were
+ the actual managed bean. This aliasing facility, much like
+ <a href="http://myfaces.apache.org">MyFaces</a>
+ <a href="http://wiki.apache.org/myfaces/AliasBean"><code>t:aliasBean</code></a>
+ tag--lets you reuse a view for many different managed beans.
+ </p>
+ </ul>
+
+ <p>The most prominent Clay feature is HTML Views. In this document, we will cover
+ that feature in some detail. For more information on composition and symbols,
+ see the Shale javadoc.<i> Todo: add sections on composition and symbols</i></p>
</subsection>
+
+ <a name="clay-view-options"/>
+ <subsection name="HTML Views">
+ <p>Back in the early days of J2EE, when people wrote applications that
+ were mostly collections of servlets, everybody implemented their views in HTML.
+ But because implementing user interfaces with print statements that originate
+ from the bowels of a servlet is just marginally better than a trip to the dentist,
+ Sun quickly followed servlets with JSP. Now, we could mix Java code with HTML
+ instead of the other way around, which as everybody knows may have seemed like a
+ step in the right direction, but was, and still is, fraught with perils of its own.</p>
+
+ <p>Anyway, the point is that we moved away from HTML with JSP tags, and later on,
+ custom tags. Of course nowadays, with the advent of JavaServer Faces, it's fashionable
+ to construct your user interface almost entirely with JSF tags. For the software
+ developer who is also a graphic designer, that's a pretty sweet deal. JSF tags are
+ much more concise than the HTML that they replace and developers can control all
+ manner of things look and feel. But what if the software developer and graphic designer
+ are different people? What then?</p>
+
+ <p>If you'd like your graphic designers to implement your look and feel while your
+ software developers work on components, then JSP is a terrible solution. I can see
+ those of you in the back of the room nodding your heads, so I won't preach to the choir.
+ The Apache <a href="">Tapestry</a> framework was the first Java web framework that not
+ only acknowledged that fact, but, more importantly, did something about it.</p>
+
+ <p>Tapestry maintains a strict separation between graphic designers and software
+ developers through an innovative design. Graphic designers mockup HTML to implement
+ a look and feel. Software developers take that HTML and add an innocuous <code>jwcid</code>
+ attribute to the HTML tags that represent dynamic content. That attribute's value
+ references a Tapestry component defined in an XML file. When the HTML is viewed as
+ an HTML page, the graphic designer sees the mockup HTML, but when the same HTML file
+ is run through Tapestry, the mockup HTML is replaced by Tapestry components. And here's
+ the clincher: <i>when the components replace the mockup HTML, they soak up the mockup HTML's
+ look and feel</i>. This lets graphic designers concentrate on the look and feel and lets
+ developers work on components. Whoa.</p>
+
+ <p>Clay unabashedly copies Tapestry's innovations. In fact, if you replace <i>Tapestry</i>
+ with <i>Clay</i> and <i>jwcid</i> with <i>jsfid</i> in the preceeding paragraph, you'd have
+ an accurate description of Clay's HTML Views. But enough talk; let's see how it works.</p>
+
+ <p>Here's yet another incarnation of a simple login and registration application:
+ (hey, it's better than Hello World!).</p>
+ <p><img src="images/login.jpg" alt="Shale Use Cases - Clay Login Example"/></p>
+ <p>This application uses Tiles, so its JSP pages are pretty well carved up. We're
+ going to look at the JSP page that implements the login form. (Don't sweat the
+ deatils)</p>
+<pre>
+<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
+<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
+<%@ taglib prefix="s" uri="http://struts.apache.org/shale/core" %>
+
+<s:token id="token"/>
+
+<h:panelGrid columns="1" styleClass="loginPanel">
+ <h:panelGrid styleClass="summaryHeadingPanel">
+ <h:outputText value="#{msgs.loginHeading}" styleClass="loginHeading"/>
+ </h:panelGrid>
+
+ <h:message for="token" styleClass="errors"/>
+
+ <h:panelGrid columns="2" styleClass="loginFields">
+ <%-- NAME FIELD --%>
+ <h:outputLabel for="name" value="#{msgs.namePrompt}"/>
+ <h:panelGroup>
+ <h:inputText size="15" id="name" onfocus="activatefield(this);"
+ onblur="deactivatefield(this);" styleClass="input"
+ validator="#{loginPage.validate}" required="true"
+ value="#{userContext.name}"/>
+ <h:message for="name" styleClass="errors"/>
+ </h:panelGroup>
+
+ <%-- PASSWORD FIELD --%>
+ <h:outputLabel for="pwd" value="#{msgs.passwordPrompt}"/>
+ <h:panelGroup>
+ <h:inputSecret size="6" id="pwd" value="#{userContext.password}"
+ onfocus="activatefield(this);" onblur="deactivatefield(this);"
+ styleClass="input"/>
+ <h:message for="pwd" styleClass="errors"/>
+ </h:panelGroup>
+ <h:outputText value=" "/><h:outputText value=" "/>
+ <h:commandButton value="#{msgs.loginButtonPrompt}"
+ action="#{loginPage.submit}"/>
+ </h:panelGrid>
+ </h:panelGrid>
+</pre>
+ <p>Quick! Turn away! Let's replace that JSP with mockup HTML tied to JSF components:</p>
+<pre>
+<html>
+ <head>
+ <title>Clay</title>
+ <link href="/shale/styles/site.css"
+ rel="stylesheet" type="text/css"/>
+ </head>
+ <body class="loginPage">
+ <table style="vertical-align: top; text-align: center; width: 100%; height: 100%">
+ <tr style="vertical-align: top;">
+ <td colspan="2" class="header">
+ <img src="/shale/graphics/powered-by-tiles.jpg" align="top"/>
+ <img src="/shale/graphics/spacer.gif" width="6px" height="1px"/>
+ <img src="/shale/graphics/shaleClay.jpg"/>
+ <img src="/shale/graphics/spacer.gif" width="6px" height="1px"/>
+ <img src="/shale/graphics/powered-by-javaserver-faces.jpg" align="top"/>
+ </td>
+ </tr>
+ </tr>
+ <form id="menuContentForm" onsubmit="return validateForm(this);">
+ <tr style="vertical-align: top; height: 100%;">
+ <td class="menu">
+ <table>
+ <tr><td>
+ <img src="/shale/graphics/china-flag-animated.gif" border="0px"/>
+ <img src="/shale/graphics/us-flag-animated.gif" border="0px"/>
+ </td></tr>
+ <tr><td>
+ <a href="#" jsfid="dialogLink">Create an account</a>
+ </td></tr>
+ </table>
+ </td>
+ </tr>
+ ...
+</pre>
+ <p>The HTML is considerably longer than the JSP, so we've only shown the top of the
+ HTML file. You don't need to see it all--all that's pertinent is the mockup HTML and
+ the <code>jsfid</code> attributes that point to JSF components. Remember that we can
+ access the preceeding HTML either directly (as a graphic designer)
+ or through Clay (as a developer or end user). Here's what it looks like when we access the
+ HTML directly: </p>
+ <p><img src="images/login-html.jpg" alt="Clay Login Example - Login with HTML"/></p>
+
+ <p>And here's what it looks like through Clay:</p>
+ <p><img src="images/login-clay.jpg" alt="Clay Login Example - Login with Clay"/></p>
+
+ <p>Did you notice the differences? First, the URL in the address bars: one ends in <code>.html</code>
+ and the other ends in <code>.faces</code>. Second, the error messages: mockups for the HTML and
+ real error messages for Clay. In the second snapshot, I attempted to login without a username, which
+ triggered JSF validation, resulting in the ensuing error message. Remember, when we run this seemingly
+ begnign HTML through Clay's transducer, we get a full blown login page, just like the
+ original page that we saw at the beginning of this section. We changed the images a bit to reflect
+ the fact that now we're using Clay, but that's a fully capable login page you're looking at. Of
+ course, that fully capable login page is populated, at runtime, by Clay with JSF components
+ that replace the HTML mockup. Cool, eh?</p>
+
+ <p>So, you wonder, where do the components come from? Well, we define them in an XML file, silly.
+ Shame on you for asking. In fact, here's the XML definition of the username <code>h:inputText</code>,
+ which is an input component:</p>
+<p>
+<pre>
+<!-- username textfield -->
+<component jsfid="username"
+ extends="inputText"
+ id="username">
+ <attributes>
+ <set name="required" value="true"/>
+ <set name="value" value="#{userContext.username}"/>
+ <set name="validator" value="#{managed-bean-name.validate}"/>
+ </attributes>
+</component>
+</pre>
+</p>
+ <p>Now, pretend you are a graphic designer and you decide to change error messages to red. Let's
+ keep it simple and just change the error message for the username field. So, you go into
+ <code>clay-login.html</code> and add a <code>style</code> attribute to the error message mockup:</p>
+<p>
+<pre>
+...
+<td>
+ <input jsfid="username" type="text" size="15"/>
+</td>
+<td>
+ <span jsfid="usernameMessage" style="color: red;">
+ username error message
+ </span>
+</td>
+...
+</pre>
+</p>
+ <p>You save that file, refresh the browser, and, as expected, you see this: (check the address bar - that's the HTML file)</p>
+
+ <p><img src="images/login-clay-red.jpg" alt="Clay Login Example - Login with Clay"/></p>
+
+ <p>Later that day, a developer runs the application through Clay and
+ Clay replaces the mockup error message with a real error message component
+ and <i>transmits the style for the mockup to the
+ component</i>. So, when an end-user runs the application and stubbornly refuses to
+ supply a username, he endures the following shame:</p>
+
+ <p><img src="images/login-html-red.jpg" alt="Shale Use Cases - Clay Login Example"/></p>
+ <p><i>Hot damn!</i> Our runtime error message is also red. Clay has transmitted the
+ <code>style</code> attribute of the mockup HTML for
+ the username error message to the component that replaces it at runtime. Now graphic
+ designers and software developers can truly lead independent lives.</p>
+
+ <p>One more thing before we conclude this section. You may wonder how Clay replaces
+ mockup HTML with components; how does Clay know to do that to our HTML page? The
+ answer is that Clay is implemented as a JSF component, and, along with our HTML
+ page, we have a JSP page that uses that component to process the HTML. That component
+ is accessed through a custom tag, like this:</p>
+<pre>
+<%@ taglib prefix="sh" uri="http://struts.apache.org/shale/clay-plugin" %>
+...
+<sh:clay id="loginTemplate" jsfid="/pages/clay-login.html"
+ managedBeanName="loginPage"/>
+</pre>
+
+ <p>In the preceeding code, we give the clay tag a <code>jsfid</code> that points, not to a
+ component, but to our HTML page instead. Notice we also specify a managed bean that's used
+ by the component definitions; for example, here's the definition of the login page's
+ submit button:</p>
+<pre>
+<!-- login submit button -->
+<component jsfid="login" extends="commandButton" id="login">
+ <attributes>
+ <set name="action" value="#{managed-bean-name.submit}"/>
+ <set name="value" value="Log In" />
+ </attributes>
+</component>
+</pre>
+ <p>The expression <code>#{managed-bean-name.submit}</code> references the managed bean we
+ specified with the <code>clay</code> tag above with the <code>managedBeanName</code>
+ attribute. You can alias managed beans for any component, which allows a single component
+ definition to be used for any managed bean that has corresponding method signatures.</p>
+
+ <p>As you might suspect, implementing Clay with a custom component gives you a great deal
+ of flexibility. Although HTML views are Clay's most visible feature, they
+ are by no means its only feature. Clay also sports Tiles-like composition and more
+ sophisticated symbol replacement than the managed bean example discussed above.</p>
+ </subsection>
<a name="clay-view-options"/>
<subsection name="Clay View Composition Options">
Added: struts/shale/trunk/xdocs/images/login-clay-red.jpg
URL: http://svn.apache.org/viewcvs/struts/shale/trunk/xdocs/images/login-clay-red.jpg?rev=376215&view=auto
==============================================================================
Binary file - no diff available.
Propchange: struts/shale/trunk/xdocs/images/login-clay-red.jpg
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Added: struts/shale/trunk/xdocs/images/login-clay.jpg
URL: http://svn.apache.org/viewcvs/struts/shale/trunk/xdocs/images/login-clay.jpg?rev=376215&view=auto
==============================================================================
Binary file - no diff available.
Propchange: struts/shale/trunk/xdocs/images/login-clay.jpg
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Added: struts/shale/trunk/xdocs/images/login-html-red.jpg
URL: http://svn.apache.org/viewcvs/struts/shale/trunk/xdocs/images/login-html-red.jpg?rev=376215&view=auto
==============================================================================
Binary file - no diff available.
Propchange: struts/shale/trunk/xdocs/images/login-html-red.jpg
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Added: struts/shale/trunk/xdocs/images/login-html.jpg
URL: http://svn.apache.org/viewcvs/struts/shale/trunk/xdocs/images/login-html.jpg?rev=376215&view=auto
==============================================================================
Binary file - no diff available.
Propchange: struts/shale/trunk/xdocs/images/login-html.jpg
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Added: struts/shale/trunk/xdocs/images/login.jpg
URL: http://svn.apache.org/viewcvs/struts/shale/trunk/xdocs/images/login.jpg?rev=376215&view=auto
==============================================================================
Binary file - no diff available.
Propchange: struts/shale/trunk/xdocs/images/login.jpg
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Modified: struts/shale/trunk/xdocs/index.xml
URL: http://svn.apache.org/viewcvs/struts/shale/trunk/xdocs/index.xml?rev=376215&r1=376214&r2=376215&view=diff
==============================================================================
--- struts/shale/trunk/xdocs/index.xml (original)
+++ struts/shale/trunk/xdocs/index.xml Wed Feb 8 23:45:42 2006
@@ -171,7 +171,10 @@
with the <a href="http://www.springframework.org">Spring Framework</a>,
allowing the use of Spring's dependency injection framework to create
JavaServer Faces managed beans.</li>
- <li><a href="features-reusable-views.html">Reusable Views</a> - An innovative
+ <li><a href="features-reusable-views.html">Clay</a> - An alternative to JSP where
+ you define views in pure HTML, in a fashion similar to <a href="http://jakarta.apache.org/tapestry/index.html">Tapestry</a>
+ and <a href="https://facelets.dev.java.net/">Facelets</a>.
+ An innovative
sub-framework for supporting the configuration of reusable subtrees
of JavaServer Faces components for customizable reuse.</li>
<li><a href="features-test-framework.html">Test Framework</a> - Set of mock objects
Modified: struts/shale/trunk/xdocs/navigation.xml
URL: http://svn.apache.org/viewcvs/struts/shale/trunk/xdocs/navigation.xml?rev=376215&r1=376214&r2=376215&view=diff
==============================================================================
--- struts/shale/trunk/xdocs/navigation.xml (original)
+++ struts/shale/trunk/xdocs/navigation.xml Wed Feb 8 23:45:42 2006
@@ -26,7 +26,7 @@
<item name="JNDI Integration" href="/features-jndi-integration.html"/>
<item name="Spring Integration" href="/features-spring-integration.html"/>
<item name="Tiles Integration" href="/features-tiles-integration.html"/>
- <item name="Reusable Views" href="/features-reusable-views.html"/>
+ <item name="Clay" href="/features-reusable-views.html"/>
<item name="Test Framework" href="/features-test-framework.html"/>
<item name="Tiger Extensions" href="/features-tiger-extensions.html"/>
</menu>
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@struts.apache.org
For additional commands, e-mail: dev-help@struts.apache.org