001: /*
002: * Version: MPL 1.1/GPL 2.0/LGPL 2.1
003: *
004: * "The contents of this file are subject to the Mozilla Public License
005: * Version 1.1 (the "License"); you may not use this file except in
006: * compliance with the License. You may obtain a copy of the License at
007: * http://www.mozilla.org/MPL/
008: *
009: * Software distributed under the License is distributed on an "AS IS"
010: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
011: * License for the specific language governing rights and limitations under
012: * the License.
013: *
014: * The Original Code is ICEfaces 1.5 open source software code, released
015: * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
016: * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
017: * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
018: *
019: * Contributor(s): _____________________.
020: *
021: * Alternatively, the contents of this file may be used under the terms of
022: * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
023: * License), in which case the provisions of the LGPL License are
024: * applicable instead of those above. If you wish to allow use of your
025: * version of this file only under the terms of the LGPL License and not to
026: * allow others to use your version of this file under the MPL, indicate
027: * your decision by deleting the provisions above and replace them with
028: * the notice and other provisions required by the LGPL License. If you do
029: * not delete the provisions above, a recipient may use your version of
030: * this file under either the MPL or the LGPL License."
031: *
032: */
033:
034: package com.icesoft.faces.application;
035:
036: import com.icesoft.faces.component.NamespacingViewRoot;
037: import com.icesoft.faces.context.BridgeExternalContext;
038: import com.icesoft.faces.context.BridgeFacesContext;
039: import com.icesoft.faces.context.DOMResponseWriter;
040: import com.icesoft.faces.webapp.http.servlet.ServletExternalContext;
041: import com.icesoft.faces.webapp.parser.ImplementationUtil;
042: import com.icesoft.faces.webapp.parser.JspPageToDocument;
043: import com.icesoft.faces.webapp.parser.Parser;
044: import com.icesoft.faces.webapp.xmlhttp.PersistentFacesCommonlet;
045: import com.icesoft.util.SeamUtilities;
046: import org.apache.commons.logging.Log;
047: import org.apache.commons.logging.LogFactory;
048:
049: import javax.faces.FacesException;
050: import javax.faces.application.StateManager;
051: import javax.faces.application.ViewHandler;
052: import javax.faces.component.NamingContainer;
053: import javax.faces.component.StateHolder;
054: import javax.faces.component.UIComponent;
055: import javax.faces.component.UIData;
056: import javax.faces.component.UIViewRoot;
057: import javax.faces.context.ExternalContext;
058: import javax.faces.context.FacesContext;
059: import javax.faces.context.ResponseWriter;
060: import javax.faces.render.RenderKitFactory;
061: import java.beans.Beans;
062: import java.io.IOException;
063: import java.io.InputStream;
064: import java.io.InputStreamReader;
065: import java.io.Reader;
066: import java.net.URI;
067: import java.net.URISyntaxException;
068: import java.net.URL;
069: import java.net.URLConnection;
070: import java.util.Arrays;
071: import java.util.Collections;
072: import java.util.HashMap;
073: import java.util.Iterator;
074: import java.util.Locale;
075: import java.util.Map;
076:
077: /**
078: * <B>D2DViewHandler</B> is the ICEfaces ViewHandler implementation
079: *
080: * @see javax.faces.application.ViewHandler
081: */
082: public class D2DViewHandler extends ViewHandler {
083: // Log instance for this class
084: protected static Log log = LogFactory.getLog(D2DViewHandler.class);
085:
086: static {
087: if (log.isInfoEnabled()) {
088: log.info(new ProductInfo().toString());//Announce ICEfaces
089: }
090: }
091:
092: private final static String DELEGATE_NONIFACE = "com.icesoft.faces.delegateNonIface";
093: private final static String ACTION_URL_SUFFIX = "com.icesoft.faces.actionURLSuffix";
094: private final static String RELOAD_INTERVAL = "com.icesoft.faces.reloadInterval";
095: private final static String DO_JSF_STATE_MANAGEMENT = "com.icesoft.faces.doJSFStateManagement";
096: public final static String INCLUDE_OPEN_AJAX_HUB = "com.icesoft.faces.openAjaxHub";
097: private final static String LAST_LOADED_KEY = "_lastLoaded";
098: private final static String LAST_CHECKED_KEY = "_lastChecked";
099: // Key for storing ICEfaces auxillary data in the session
100: public static final String DOM_CONTEXT_TABLE = "com.icesoft.faces.sessionAuxiliaryData";
101: public static final String CHAR_ENCODING = "UTF-8";
102: public static final String HTML_CONTENT_TYPE = "text/html;charset="
103: + CHAR_ENCODING;
104:
105: public static final String DEFAULT_VIEW_ID = "default";
106:
107: private String actionURLSuffix;
108: protected boolean delegateNonIface = false;
109: protected boolean delegateNonIfaceDefault = false;
110: protected boolean jsfStateManagement;
111: //reloadInterval internally in milliseconds
112: protected long reloadInterval;
113: protected long reloadIntervalDefault = 2;
114: private boolean parametersInitialized = false;
115:
116: protected Parser parser;
117: private ViewHandler delegate;
118:
119: public D2DViewHandler() {
120: try {
121: InputStream inputStream = this .getClass()
122: .getResourceAsStream(
123: "serializedTagToComponentMapFull.ser");
124: parser = new Parser(inputStream);
125: } catch (IOException e) {
126: throw new RuntimeException(e);
127: }
128: }
129:
130: public D2DViewHandler(ViewHandler delegate) {
131: this ();
132: this .delegate = delegate;
133: }
134:
135: // Render the components
136: public void renderView(FacesContext context, UIViewRoot viewToRender)
137: throws IOException, FacesException {
138: initializeParameters(context);
139: if (delegateView(viewToRender.getViewId())) {
140: delegate.renderView(context, viewToRender);
141: return;
142: }
143:
144: if (log.isTraceEnabled()) {
145: log.trace("renderView(FC,UIVR) BEFORE renderResponse "
146: + "viewToRender.getViewId(): "
147: + viewToRender.getViewId());
148: }
149: renderResponse(context);
150:
151: if (jsfStateManagement) {
152: StateManager stateMgr = context.getApplication()
153: .getStateManager();
154: stateMgr.saveSerializedView(context);
155: // JSF 1.1 removes transient components here, but I don't think that 1.2 does
156: }
157: }
158:
159: /**
160: * Create a new ViewRoot
161: *
162: * @param context FacesContext
163: * @param viewId ViewId identifying the root
164: * @return A new viewRoot
165: */
166: public UIViewRoot createView(FacesContext context, String viewId) {
167: initializeParameters(context);
168:
169: if (delegateView(viewId)) {
170: return delegate.createView(context, viewId);
171: }
172:
173: UIViewRoot root = new NamespacingViewRoot(context);
174: // UIViewRoot root = new UIViewRoot();
175: root.setRenderKitId(RenderKitFactory.HTML_BASIC_RENDER_KIT);
176:
177: Map contextServletTable = getContextServletTable(context);
178: if (null == viewId) {
179: root.setViewId("default");
180: context.setViewRoot(root);
181: contextServletTable.put(
182: DOMResponseWriter.RESPONSE_VIEWROOT, root);
183: Locale locale = calculateLocale(context);
184: root.setLocale(locale);
185: return root;
186: }
187:
188: root.setViewId(viewId);
189: context.setViewRoot(root);
190: contextServletTable.put(DOMResponseWriter.RESPONSE_VIEWROOT,
191: root);
192:
193: return root;
194: }
195:
196: /**
197: * Restore the view if possible. This method can return null if
198: * no ViewRoot is available. The <code>LifeCycle</code> will call
199: * createView in this case.
200: *
201: * @param context FacesContext
202: * @param viewId ViewId identifying the view to restore
203: * @return UIViewRoot instance if found, null if none yet created,
204: * or if trying to model Seam JSF behaviour.
205: */
206: public UIViewRoot restoreView(FacesContext context, String viewId) {
207: this .initializeParameters(context);
208:
209: if (delegateView(viewId)) {
210: return delegate.restoreView(context, viewId);
211: }
212:
213: UIViewRoot currentRoot = context.getViewRoot();
214: //MyFaces expects path to match current view
215: ExternalContext externalContext = context.getExternalContext();
216:
217: if (externalContext instanceof ServletExternalContext) {
218:
219: ServletExternalContext servletExternalContext = (ServletExternalContext) externalContext;
220:
221: servletExternalContext.setRequestServletPath(viewId);
222:
223: if (null != externalContext.getRequestPathInfo()) {
224: //it's not null, so must be valid to keep in synch for MyFaces
225: servletExternalContext.setRequestPathInfo(viewId);
226: }
227:
228: if (SeamUtilities.isSeamEnvironment()) {
229: if (servletExternalContext
230: .getRequestParameterMap()
231: .remove(
232: PersistentFacesCommonlet.SEAM_LIFECYCLE_SHORTCUT) != null) {
233:
234: if (log.isTraceEnabled()) {
235: log
236: .trace("Seam Keyword shortcut found, new ViewRoot");
237: }
238: return null;
239: } else {
240: if (log.isTraceEnabled()) {
241: log.trace("No Seam Keyword shortcut found");
242: }
243: }
244: }
245: }
246:
247: if (null != currentRoot
248: && mungeViewId(viewId).equals(
249: mungeViewId(currentRoot.getViewId()))) {
250: // purgeSeamContexts(context, currentRoot);
251: return currentRoot;
252: }
253:
254: Map contextServletTable = getContextServletTable(context);
255: Map domResponseContexts;
256: if (contextServletTable
257: .containsKey(DOMResponseWriter.RESPONSE_CONTEXTS_TABLE)) {
258: domResponseContexts = (Map) contextServletTable
259: .get(DOMResponseWriter.RESPONSE_CONTEXTS_TABLE);
260: } else {
261: //todo: figure out what these maps are doing and re-implement functionality
262: domResponseContexts = Collections
263: .synchronizedMap(new HashMap());
264: contextServletTable.put(
265: DOMResponseWriter.RESPONSE_CONTEXTS_TABLE,
266: domResponseContexts);
267: }
268:
269: UIViewRoot root = null;
270:
271: root = (UIViewRoot) contextServletTable
272: .get(DOMResponseWriter.RESPONSE_VIEWROOT);
273:
274: if ((null != root)
275: && (null != viewId)
276: && (mungeViewId(viewId).equals(mungeViewId(root
277: .getViewId())))) {
278: }
279:
280: return root;
281: }
282:
283: private static Map getContextServletTables(FacesContext context) {
284: Map sessionMap = context.getExternalContext().getSessionMap();
285: String viewNumber = "-";
286: if (context instanceof BridgeFacesContext) {
287: viewNumber = ((BridgeFacesContext) context).getViewNumber();
288: }
289:
290: String treeKey = viewNumber + "/" + DOM_CONTEXT_TABLE;
291: Map contextTable;
292: if (sessionMap.containsKey(treeKey)) {
293: contextTable = (Map) sessionMap.get(treeKey);
294: } else {
295: contextTable = new HashMap();
296: sessionMap.put(treeKey, contextTable);
297: }
298:
299: return contextTable;
300: }
301:
302: public static Map getContextServletTable(FacesContext context) {
303: Map domContextTables = getContextServletTables(context);
304: String servletRequestPath = getServletRequestPath(context);
305: if (domContextTables.containsKey(servletRequestPath)) {
306: return (Map) domContextTables.get(servletRequestPath);
307: } else {
308: Map domContextTable = Collections
309: .synchronizedMap(new HashMap());
310: domContextTables.put(servletRequestPath, domContextTable);
311: return domContextTable;
312: }
313: }
314:
315: public static String getServletRequestPath(FacesContext context) {
316: if (Beans.isDesignTime()) {
317: //IDE scenario requires artificial path
318: return context.getViewRoot().getViewId();
319: }
320: ExternalContext externalContext = context.getExternalContext();
321: if (externalContext instanceof BridgeExternalContext) {
322: String uri = ((BridgeExternalContext) externalContext)
323: .getRequestURI();
324: if (null == uri) {
325: if (log.isWarnEnabled()) {
326: log.warn("Failing over to default request path");
327: }
328: uri = "default";
329:
330: }
331: return uri;
332: }
333: return (externalContext.getRequestContextPath() + externalContext
334: .getRequestServletPath());
335: }
336:
337: public static String getServletRequestPath(
338: ExternalContext externalContext, String viewId) {
339: if (externalContext == null) {
340: throw new IllegalStateException("ExternalContext is null");
341: }
342: return externalContext.getRequestContextPath() + viewId;
343: }
344:
345: public String getActionURL(FacesContext context, String viewId) {
346: //Maybe should always use delegate
347: if (delegateView(viewId)) {
348: return delegate.getActionURL(context, viewId);
349: }
350:
351: if (viewId.indexOf("://") >= 0) {
352: return viewId;
353: }
354:
355: if (viewId.charAt(0) != '/') {
356: throw new IllegalArgumentException("viewId " + viewId
357: + "does not begin with '/'");
358: }
359:
360: //remove extension and replace with parametrized suffix
361: if (null != actionURLSuffix) {
362: viewId = viewId.substring(0, viewId.lastIndexOf("."))
363: + actionURLSuffix;
364: }
365:
366: return context.getExternalContext().getRequestContextPath()
367: + viewId;
368: }
369:
370: public String getResourceURL(FacesContext context, String path) {
371: //Context and resource must be non-null
372: if (context == null) {
373: throw new IllegalArgumentException("context cannot be null");
374: }
375:
376: if (path == null) {
377: throw new IllegalArgumentException("path cannot be null");
378: }
379:
380: ExternalContext externalContext = context.getExternalContext();
381: //The URI class doesn't like resource with illegal characters so
382: //we need to do our own encoding.
383: String resourcePath = path.trim().replaceAll(" ", "%20");
384: // Components that render out links to resources like images, CSS,
385: // JavaScript, etc. must do it correctly.
386: String contextPath = externalContext.getRequestContextPath();
387:
388: if (resourcePath.startsWith("/")) {
389: //absolute references are handled differently, we just
390: //need to stick the context in front.
391: return contextPath + resourcePath;
392: } else {
393: //for relative paths, we need to resolve them to the full path
394: String servletPath = externalContext
395: .getRequestServletPath();
396: String base = contextPath + servletPath;
397: try {
398: URI baseURI = new URI(base);
399: URI resourceURI = new URI(resourcePath);
400: URI resolvedURI = baseURI.resolve(resourceURI);
401: return resolvedURI.toString();
402: } catch (URISyntaxException e) {
403: if (log.isWarnEnabled()) {
404: log.warn("could not resolve URI's based on"
405: + "\n context : " + contextPath
406: + "\n path : " + servletPath
407: + "\n resource: " + resourcePath, e);
408: }
409: return resourcePath;
410: }
411: }
412: }
413:
414: protected long getTimeAttribute(UIComponent root, String key) {
415: Long timeLong = (Long) root.getAttributes().get(key);
416: long time = (null == timeLong) ? 0 : timeLong.longValue();
417: return time;
418: }
419:
420: protected void renderResponse(FacesContext facesContext)
421: throws IOException {
422: BridgeFacesContext context = (BridgeFacesContext) facesContext;
423: UIViewRoot root = context.getViewRoot();
424: String viewId = root.getViewId();
425:
426: if (log.isTraceEnabled()) {
427: log.trace("Rendering " + root + " with "
428: + root.getChildCount() + " children");
429: }
430:
431: clearSession(context);
432: ResponseWriter responseWriter = context
433: .createAndSetResponseWriter();
434:
435: boolean reloadView = false;
436: URLConnection viewConnection = null;
437:
438: if ((root.getChildCount() == 0) || (reloadInterval > -1)) {
439: // We have not parsed the page yet;
440: // Need an input stream for the page;
441: if (viewId.startsWith("/faces")) {
442: viewId = viewId.substring(6);
443: }
444: if (viewId.endsWith(".jpg") || viewId.endsWith(".gif")
445: || viewId.endsWith(".png")) {
446: context.getExternalContext().dispatch(viewId);
447: return;
448: }
449: try {
450: URL viewURL = context.getExternalContext().getResource(
451: viewId);
452: if (null == viewURL) {
453: if (viewId.endsWith(".faces")) {
454: viewId = truncate(".faces", viewId);
455: } else if (viewId.endsWith(".jsf")) {
456: viewId = truncate(".jsf", viewId);
457: } else if (viewId.endsWith(".iface")) {
458: viewId = truncate(".iface", viewId);
459: } else if (viewId.endsWith(".jsp")) {
460: //MyFaces thinks everything is a .jsp
461: viewId = truncate(".jsp", viewId);
462: }
463:
464: viewId = viewId + ".jspx";
465: viewURL = context.getExternalContext().getResource(
466: viewId);
467: }
468:
469: if (null == viewURL) {
470: if (viewId.endsWith(".jspx")) {
471: viewId = truncate(".jspx", viewId) + ".jsp";
472: }
473: viewURL = context.getExternalContext().getResource(
474: viewId);
475: }
476:
477: long currentTime = System.currentTimeMillis();
478: long lastLoaded = getTimeAttribute(root,
479: LAST_LOADED_KEY);
480: long lastChecked = getTimeAttribute(root,
481: LAST_CHECKED_KEY);
482: long lastModified = 0;
483:
484: //newly instantiated viewRoot will have lastChecked of 0
485: //and lastLoaded of 0
486: if (currentTime > lastChecked + reloadInterval) {
487: viewConnection = viewURL.openConnection();
488: lastModified = viewConnection.getLastModified();
489: root.getAttributes().put(LAST_CHECKED_KEY,
490: new Long(currentTime));
491: if (lastModified > lastLoaded) {
492: reloadView = true;
493: }
494: }
495:
496: } catch (Exception e) {
497: throw new FacesException("Can't find stream for "
498: + viewId, e);
499: }
500: }
501:
502: if (reloadView) {
503: Reader viewInput = null;
504:
505: try {
506: viewInput = new InputStreamReader(viewConnection
507: .getInputStream(), CHAR_ENCODING);
508: if (viewId.endsWith(".jsp")) {
509: viewInput = JspPageToDocument.transform(viewInput);
510: } else if (viewId.endsWith(".jspx")) {
511: viewInput = JspPageToDocument
512: .preprocessJspDocument(viewInput);
513: }
514: } catch (Throwable e) {
515: throw new FacesException("Can't read stream for "
516: + viewId, e);
517: }
518:
519: // Parse the page;
520: try {
521: //TODO: pass viewInput as an InputStream in order to give to the XML parser a chance to
522: //TODO: read the encoding type declared in the xml processing instruction (<?xml version="1.0" charset="..."?>)
523: parser.parse(viewInput, context);
524: root.getAttributes().put(LAST_LOADED_KEY,
525: new Long(System.currentTimeMillis()));
526:
527: } catch (Throwable e) {
528: throw new FacesException("Can't parse stream for "
529: + viewId + " " + e.getMessage(), e);
530: }
531:
532: if (ImplementationUtil.isJSF12()) {
533: if (log.isDebugEnabled()) {
534: log.debug("Rendering outside ViewTag for JSF 1.2");
535: }
536: //JSF 1.2 ViewTag does not invoke rendering
537: responseWriter.startDocument();
538: renderResponse(context, root);
539: responseWriter.endDocument();
540: }
541:
542: } else {
543: responseWriter.startDocument();
544: renderResponse(context, root);
545: responseWriter.endDocument();
546: tracePrintComponentTree(context);
547: }
548:
549: }
550:
551: protected void renderResponse(FacesContext context,
552: UIComponent component) throws IOException {
553: if (!component.isRendered())
554: return;
555:
556: // UIViewRoot.encodeBegin(FacesContext) resets its counter for
557: // createUniqueId(), which we don't want, or else we get
558: // duplicate ids
559: boolean isUIViewRoot = component instanceof UIViewRoot;
560: if (!isUIViewRoot)
561: component.encodeBegin(context);
562:
563: if (component.getRendersChildren()) {
564: component.encodeChildren(context);
565: } else {
566: Iterator kids = component.getChildren().iterator();
567: while (kids.hasNext()) {
568: renderResponse(context, (UIComponent) kids.next());
569: }
570: }
571:
572: if (!isUIViewRoot)
573: component.encodeEnd(context);
574:
575: //Workaround so that MyFaces UIData will apply values to
576: //child components even if the tree is not restored via StateManager
577: if (component instanceof javax.faces.component.UIData) {
578: StateHolder stateHolder = (StateHolder) component;
579: stateHolder.restoreState(context, stateHolder
580: .saveState(context));
581: }
582: }
583:
584: protected void tracePrintComponentTree(FacesContext context) {
585: tracePrintComponentTree(context, context.getViewRoot());
586: }
587:
588: protected void tracePrintComponentTree(FacesContext context,
589: UIComponent component) {
590: if (log.isTraceEnabled()) {
591: StringBuffer sb = new StringBuffer(4096);
592: sb.append("tracePrintComponentTree() vvvvvv\n");
593: tracePrintComponentTree(context, component, 0, sb, null);
594: log.trace(sb.toString());
595: log.trace("tracePrintComponentTree() ^^^^^^");
596: }
597: }
598:
599: private void tracePrintComponentTree(FacesContext context,
600: UIComponent component, int levels, StringBuffer sb,
601: String facetName) {
602: if (component == null) {
603: sb.append("null\n");
604: return;
605: }
606: final String PREFIX_WHITESPACE = " ";
607: StringBuffer prefix = new StringBuffer(64);
608: for (int i = 0; i < levels; i++)
609: prefix.append(PREFIX_WHITESPACE);
610: prefix.append("<");
611: String compStr = component.toString();
612:
613: StringBuffer open = new StringBuffer(512);
614: open.append(prefix);
615: open.append(compStr);
616: Map facetsMap = component.getFacets();
617: boolean hasKids = component.getChildCount() > 0;
618: boolean hasFacets = (facetsMap != null)
619: && (facetsMap.size() > 0);
620: boolean hasUnderlings = hasKids | hasFacets;
621: if (!hasUnderlings)
622: open.append("/");
623: open.append(">");
624: if (facetName != null) {
625: open.append(" facetName: ");
626: open.append(facetName);
627: }
628: open.append(" id: ");
629: open.append(component.getId());
630: if (component.getParent() != null) {
631: open.append(" clientId: ");
632: open.append(component.getClientId(context));
633: }
634: if (hasKids) {
635: open.append(" kids: ");
636: open.append(Integer.toString(component.getChildCount()));
637: }
638: if (hasFacets) {
639: open.append(" facets: ");
640: open.append(Integer.toString(facetsMap.size()));
641: }
642: if (component.isTransient())
643: open.append(" TRANSIENT ");
644: sb.append(open.toString());
645: sb.append('\n');
646:
647: if (hasUnderlings) {
648: if (hasFacets) {
649: Object[] facetKeys = facetsMap.keySet().toArray();
650: Arrays.sort(facetKeys);
651: for (int i = 0; i < facetKeys.length; i++) {
652: tracePrintComponentTree(context,
653: (UIComponent) facetsMap.get(facetKeys[i]),
654: levels + 1, sb, facetKeys[i].toString());
655: }
656: }
657: if (hasKids) {
658: Iterator kids = component.getChildren().iterator();
659: while (kids.hasNext()) {
660: tracePrintComponentTree(context, (UIComponent) kids
661: .next(), levels + 1, sb, null);
662: }
663: }
664:
665: StringBuffer close = new StringBuffer(512);
666: close.append(prefix);
667: close.append("/");
668: close.append(compStr);
669: close.append(">");
670: sb.append(close);
671: sb.append('\n');
672: }
673: }
674:
675: protected void clearSession(FacesContext context) {
676: Map contextServletTable = getContextServletTable(context);
677: contextServletTable
678: .remove(DOMResponseWriter.RESPONSE_CONTEXTS_TABLE);
679: contextServletTable.remove(DOMResponseWriter.RESPONSE_VIEWROOT);
680: contextServletTable.remove(DOMResponseWriter.RESPONSE_DOM);
681: }
682:
683: public void writeState(FacesContext context) throws IOException {
684: if (delegateView(context.getViewRoot().getViewId())) {
685: delegate.writeState(context);
686: }
687: }
688:
689: public Locale calculateLocale(FacesContext context) {
690: Iterator locales = context.getExternalContext()
691: .getRequestLocales();
692:
693: while (locales.hasNext()) {
694: Locale locale = (Locale) locales.next();
695: Iterator supportedLocales = context.getApplication()
696: .getSupportedLocales();
697:
698: while (supportedLocales.hasNext()) {
699: Locale supportedLocale = (Locale) supportedLocales
700: .next();
701: if (locale.getLanguage().equals(
702: supportedLocale.getLanguage())) {
703:
704: if ((null == supportedLocale.getCountry())
705: || ("".equals(supportedLocale.getCountry()))) {
706: return supportedLocale;
707: }
708:
709: if (locale.equals(supportedLocale)) {
710: return supportedLocale;
711: }
712:
713: }
714: }
715: }
716:
717: Locale defaultLocale = context.getApplication()
718: .getDefaultLocale();
719: return (null == defaultLocale) ? Locale.getDefault()
720: : defaultLocale;
721: }
722:
723: public String calculateRenderKitId(FacesContext context) {
724: return delegate.calculateRenderKitId(context);
725: }
726:
727: public static boolean isValueReference(String value) {
728: if ((value.indexOf("#{") != -1)
729: && (value.indexOf("#{") < value.indexOf('}'))) {
730: return true;
731: }
732: return false;
733: }
734:
735: /**
736: * A dumber version (that can't find child components of the UIData
737: * component) of this method resides in UIComponentBase. The same is true
738: * for the private findComoponent in UIComponentBase - it is duplicated
739: * here.
740: *
741: * @param clientId
742: * @param base
743: */
744: public static UIComponent findComponent(String clientId,
745: UIComponent base) {
746: // Set base, the parent component whose children are searched, to be the
747: // nearest parent that is either 1) the view root if the id expression
748: // is absolute (i.e. starts with the delimiter) or 2) the nearest parent
749: // NamingContainer if the expression is relative (doesn't start with
750: // the delimiter)
751: String delimeter = String
752: .valueOf(NamingContainer.SEPARATOR_CHAR);
753: if (clientId.startsWith(delimeter)) {
754: // Absolute searches start at the root of the tree
755: while (base.getParent() != null) {
756: base = base.getParent();
757: }
758: // Treat remainder of the expression as relative
759: clientId = clientId.substring(1);
760: } else {
761: // Relative expressions start at the closest NamingContainer or root
762: while (base.getParent() != null) {
763: if (base instanceof NamingContainer) {
764: break;
765: }
766: base = base.getParent();
767: }
768: }
769: // Evaluate the search expression (now guaranteed to be relative)
770: String id = null;
771: UIComponent result = null;
772: while (clientId.length() > 0) {
773: int separator = clientId
774: .indexOf(NamingContainer.SEPARATOR_CHAR);
775: if (base instanceof UIData) {
776: if (separator >= 0) {
777: clientId = clientId.substring(separator + 1);
778: }
779: separator = clientId
780: .indexOf(NamingContainer.SEPARATOR_CHAR);
781: }
782: if (separator >= 0) {
783: id = clientId.substring(0, separator);
784: clientId = clientId.substring(separator + 1);
785: } else {
786: id = clientId;
787: clientId = "";
788: }
789: result = findComponent(base, id);
790: if ((result == null) || (clientId.length() == 0)) {
791: break; // Missing intermediate node or this is the last node
792: }
793: if (result instanceof NamingContainer) {
794: result = findComponent(clientId, result);
795: break;
796: }
797: }
798:
799: return result;
800: }
801:
802: private static String truncate(String remove, String input) {
803: return input.substring(0, input.length() - remove.length());
804: }
805:
806: //Determine whether handling of the view should be delegated to
807: //the delegate ViewHandler
808: private boolean delegateView(String viewId) {
809: Map requestMap = FacesContext.getCurrentInstance()
810: .getExternalContext().getRequestMap();
811: //If it's served by the PersistentFacesServlet, do not
812: //delegate
813: if (PersistentFacesCommonlet.PERSISTENT.equals(requestMap
814: .get(PersistentFacesCommonlet.SERVLET_KEY))) {
815: return false;
816: }
817: if (delegateNonIface) {
818: return (!viewId.endsWith(".iface"));
819: }
820: return false;
821: }
822:
823: private void initializeParameters(FacesContext context) {
824: if (parametersInitialized) {
825: return;
826: }
827:
828: ExternalContext ec = context.getExternalContext();
829: String delegateNonIfaceParameter = ec
830: .getInitParameter(DELEGATE_NONIFACE);
831: String reloadIntervalParameter = ec
832: .getInitParameter(RELOAD_INTERVAL);
833: String jsfStateManagementParameter = ec
834: .getInitParameter(DO_JSF_STATE_MANAGEMENT);
835: actionURLSuffix = ec.getInitParameter(ACTION_URL_SUFFIX);
836: delegateNonIface = delegateNonIfaceParameter == null ? delegateNonIfaceDefault
837: : Boolean.valueOf(delegateNonIfaceParameter)
838: .booleanValue();
839: try {
840: reloadInterval = Long.parseLong(reloadIntervalParameter) * 1000;
841: } catch (NumberFormatException e) {
842: reloadInterval = reloadIntervalDefault * 1000;
843: }
844: jsfStateManagement = Boolean.valueOf(
845: jsfStateManagementParameter).booleanValue();
846: if (!jsfStateManagement) {
847: log.debug("JSF State Management not provided");
848: }
849: parametersInitialized = true;
850: }
851:
852: //MyFaces processes the viewId before passing it to the ViewHandler
853: //so we have no idea of knowing what it really is. This function
854: //mimics the MyFaces process so we can compare ids modulo it
855: private static String mungeViewId(String viewId) {
856: String defaultSuffix = FacesContext.getCurrentInstance()
857: .getExternalContext().getInitParameter(
858: ViewHandler.DEFAULT_SUFFIX_PARAM_NAME);
859: String suffix = defaultSuffix != null ? defaultSuffix
860: : ViewHandler.DEFAULT_SUFFIX;
861:
862: int dot = viewId.lastIndexOf('.');
863: if (dot == -1) {
864: log
865: .error("Assumed extension mapping, but there is no extension in "
866: + viewId);
867: } else {
868: viewId = viewId.substring(0, dot) + suffix;
869: }
870:
871: return viewId;
872: }
873:
874: private static UIComponent findComponent(UIComponent uiComponent,
875: String componentId) {
876: UIComponent component = null;
877: UIComponent child = null;
878:
879: if (componentId.equals(uiComponent.getId())) {
880: return uiComponent;
881: }
882: Iterator children = uiComponent.getFacetsAndChildren();
883: while (children.hasNext() && (component == null)) {
884: child = (UIComponent) children.next();
885: if (!(child instanceof NamingContainer)) {
886: component = findComponent(child, componentId);
887: if (component != null) {
888: break;
889: }
890: } else if (componentId.endsWith(child.getId())) {
891: component = child;
892: break;
893: }
894: }
895: return component;
896: }
897: }
|