/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
// XulViewHandlerImpl.java
package nonjsp.application;
import javax.faces.FacesException;
import javax.faces.FactoryFinder;
import javax.faces.application.ViewHandler;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.render.RenderKit;
import javax.faces.render.RenderKitFactory;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import org.apache.commons.digester.Digester;
import org.apache.commons.digester.RuleSetBase;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.logging.impl.SimpleLog;
import nonjsp.util.RIConstants;
/**
* <B>XulViewHandlerImpl</B> is the Xul non-JSP ViewHandler implementation
*
* @see javax.faces.application.ViewHandler
*/
public class XulViewHandlerImpl extends ViewHandler {
// Log instance for this class
protected static Log log = LogFactory.getLog(XulViewHandlerImpl.class);
protected static final String CHAR_ENCODING = "ISO-8859-1";
protected static final String CONTENT_TYPE = "text/html";
//PENDING(rogerk) maybe config file?
/** Should we use a validating XML parser to read the configuration file? */
protected boolean validate = false;
/**
* The set of public identifiers, and corresponding resource names, for
* the versions of the configuration file DTDs that we know about. There
* <strong>MUST</strong> be an even number of Strings in this list!
* Only used if you are validating against DTD.
* Could be read from config file instead.
*/
protected String registrations[] = {
"-//UIT//DTD UIML 2.0 Draft//EN",
"UIML2_0d.dtd"
};
// Relationship Instance Variables
protected XmlDialectProvider dialectProvider = null;
public XulViewHandlerImpl() {
super();
dialectProvider = new XulDialectProvider();
}
// Render the components
public void renderView(FacesContext context,
UIViewRoot viewToRender) throws IOException,
FacesException {
if (context == null || viewToRender == null) {
throw new NullPointerException("RenderView: FacesContext is null");
}
RequestDispatcher requestDispatcher = null;
log.trace("Determine View Identifier And Build View...");
String viewId = viewToRender.getViewId();
HttpServletResponse response = (HttpServletResponse)
(context.getExternalContext().getResponse());
log.trace("Set ResponseWriter in FacesContext");
RenderKitFactory factory = (RenderKitFactory)
FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
RenderKit renderKit = factory.getRenderKit(context,
RenderKitFactory.HTML_BASIC_RENDER_KIT);
ResponseWriter writer = renderKit.createResponseWriter(
response.getWriter(), CONTENT_TYPE, CHAR_ENCODING);
context.setResponseWriter(writer);
response.setContentType(CONTENT_TYPE);
log.trace("Call encode methods on components");
createHeader(context);
renderResponse(context);
createFooter(context);
log.trace("Save the view and locale in the session");
Map sessionMap = getSessionMap(context);
sessionMap.put(RIConstants.REQUEST_LOCALE,
context.getViewRoot().getLocale());
sessionMap.put(javax.faces.render.ResponseStateManager.VIEW_STATE_PARAM,
context.getViewRoot());
}
public UIViewRoot restoreView(FacesContext context, String viewId) {
if (context == null) {
throw new NullPointerException("RestoreView: FacesContext is null");
}
if (log.isTraceEnabled()) {
log.trace("viewId: " + viewId);
}
UIViewRoot root = null;
InputStream viewInput = null;
RuleSetBase ruleSet = null;
root = new UIViewRoot();
root.setRenderKitId(RenderKitFactory.HTML_BASIC_RENDER_KIT);
if (null == viewId) {
// PENDING(edburns): need name for default view
// PENDING(rogerk) : what to specify for page url
// (last parameter)????
root.setViewId("default");
context.setViewRoot(root);
Locale locale = calculateLocale(context);
root.setLocale(locale);
return root;
}
try {
viewInput =
context.getExternalContext().getResourceAsStream(viewId);
if (null == viewInput) {
throw new NullPointerException();
}
} catch (Throwable e) {
throw new FacesException("Can't get stream for " + viewId, e);
}
// PENDING(edburns): can this digester instance be maintained as an
// ivar?
Digester digester = new Digester();
// SimpleLog implements the Log interface (from commons.logging).
// This replaces deprecated "Digester.setDebug" method.
// PENDING(rogerk) Perhaps the logging level should be configurable..
// For debugging, you can set the log level to
// "SimpleLog.LOG_LEVEL_DEBUG".
//
SimpleLog sLog = new SimpleLog("digesterLog");
sLog.setLevel(SimpleLog.LOG_LEVEL_ERROR);
digester.setLogger(sLog);
digester.setNamespaceAware(true);
digester.setValidating(validate);
ruleSet = dialectProvider.getRuleSet();
digester.addRuleSet(ruleSet);
if (validate) {
for (int i = 0; i < registrations.length; i += 2) {
URL url = this.getClass().getResource(registrations[i + 1]);
if (url != null) {
digester.register(registrations[i], url.toString());
}
}
}
digester.push(root);
try {
root = (UIViewRoot) digester.parse(viewInput);
} catch (Throwable e) {
throw new FacesException("Can't parse stream for " + viewId, e);
}
//Print view for debugging
if (log.isDebugEnabled()) {
printView(root);
}
root.setViewId(viewId);
context.setViewRoot(root);
return root;
}
public UIViewRoot createView(FacesContext context, String viewId) {
if (context == null) {
throw new NullPointerException("CreateView: FacesContext is null");
}
return restoreView(context, viewId);
}
public String getActionURL(FacesContext context, String viewId) {
if (viewId.charAt(0) != '/') {
throw new IllegalArgumentException(
"Illegal view ID " + viewId + ". the ID must begin with '/'");
}
// PENDING(edburns): do a more complete implementation that
// deals with the vagaries of prefix and suffix mapping. For
// now , just slap "/faces" onto the front.
if (!viewId.startsWith("/faces")) {
viewId = "/faces" + viewId;
}
return context.getExternalContext().getRequestContextPath() + viewId;
}
public String getResourceURL(FacesContext context, String path) {
if (path.startsWith("/")) {
return context.getExternalContext().getRequestContextPath() + path;
} else {
return (path);
}
}
// Create the header components for this page
private void createHeader(FacesContext context) throws IOException {
ResponseWriter writer = context.getResponseWriter();
writer.startElement("html", null);
writer.writeText("\n", null);
writer.startElement("head", null);
writer.writeText("\n", null);
writer.startElement("title", null);
writer.writeText(context.getExternalContext().getRequestContextPath(),
null);
writer.endElement("title");
writer.writeText("\n", null);
writer.endElement("head");
writer.writeText("\n", null);
writer.startElement("body", null);
writer.writeText("\n", null);
}
// Create the footer components for this page
private void createFooter(FacesContext context) throws IOException {
ResponseWriter writer = context.getResponseWriter();
writer.endElement("body");
writer.writeText("\n", null);
writer.endElement("html");
writer.writeText("\n", null);
}
// Render the response content for the completed page
private void renderResponse(FacesContext context) throws IOException {
UIComponent root = context.getViewRoot();
if (log.isTraceEnabled()) {
log.trace(
"Rendering " + root + " with " + root.getChildCount() +
" children");
}
renderResponse(context, root);
}
// Render the response content for an individual component
private void renderResponse(FacesContext context, UIComponent component)
throws IOException {
if (log.isTraceEnabled()) {
log.trace("Render Begin: " + component.getId());
}
component.encodeBegin(context);
if (component.getRendersChildren()) {
component.encodeChildren(context);
} else {
Iterator kids = component.getChildren().iterator();
while (kids.hasNext()) {
renderResponse(context, (UIComponent) kids.next());
}
}
if (log.isTraceEnabled()) {
log.trace("Render End: " + component.getId());
}
component.encodeEnd(context);
}
private Map getSessionMap(FacesContext context) {
if (context == null) {
context = FacesContext.getCurrentInstance();
}
Map sessionMap = context.getExternalContext().getSessionMap();
if (sessionMap == null) {
context.getExternalContext().getSession(true);
sessionMap = context.getExternalContext().getSessionMap();
}
return sessionMap;
}
private void printView(UIComponent uic) {
Iterator kids = uic.getChildren().iterator();
while (kids.hasNext()) {
printView((UIComponent) kids.next());
}
log.debug("VIEW: " + uic.getId());
}
public void writeState(FacesContext context) throws IOException {
}
public Locale calculateLocale(FacesContext context) {
Locale result = null;
// determine the locales that are acceptable to the client based on the
// Accept-Language header and the find the best match among the
// supported locales specified by the client.
Enumeration e = ((ServletRequest)
context.getExternalContext().getRequest()).getLocales();
while (e.hasMoreElements()) {
Locale perf = (Locale) e.nextElement();
result = findMatch(context, perf);
if (result != null) {
break;
}
}
// no match is found.
if (result == null) {
if (context.getApplication().getDefaultLocale() == null) {
result = Locale.getDefault();
} else {
result = context.getApplication().getDefaultLocale();
}
}
return result;
}
public String calculateRenderKitId(FacesContext context) {
return null;
}
/**
* Attempts to find a matching locale based on <code>perf></code> and
* list of supported locales, using the matching algorithm
* as described in JSTL 8.3.2.
*/
protected Locale findMatch(FacesContext context, Locale perf) {
Locale result = null;
Iterator it = context.getApplication().getSupportedLocales();
while (it.hasNext()) {
Locale supportedLocale = (Locale) it.next();
if (perf.equals(supportedLocale)) {
// exact match
result = supportedLocale;
break;
} else {
// Make sure the preferred locale doesn't have country set, when
// doing a language match, For ex., if the preferred locale is
// "en-US", if one of supported locales is "en-UK", even though
// its language matches that of the preferred locale, we must
// ignore it.
if (perf.getLanguage().equals(supportedLocale.getLanguage()) &&
supportedLocale.getCountry().equals("")) {
result = supportedLocale;
}
}
}
return result;
}
}