Re: Problem with BAction
Denny Chambers <[email protected]> Mon, 18 Apr 2005 16:39:08 -0500
| Newsgroups | gmane.comp.java.enhydra.barracuda.general |
|---|---|
| Message-ID | <[email protected]> |
Christian,
I suggest the following changes in BComponent and
DefaultServletRequestWrapper.java (Files are attatched):
[dchambers@honeybee core]$ svn diff comp/BComponent.java
Index: comp/BComponent.java
===================================================================
--- comp/BComponent.java (revision 76)
+++ comp/BComponent.java (working copy)
@@ -47,7 +47,7 @@
//constants
public static final String VISIBILITY_MARKER = "visdom";
- public static String DEFAULT_ENCODING = "UTF-8"; //csc_041805_1
+ public static String DEFAULT_ENCODING =
System.getProperty("file.encoding"); //ndc_041805_1
//private vars
//NOTE: the name must remain null unless specifically set; setting the
name
[dchambers@honeybee core]$ svn diff
helper/servlet/DefaultServletRequestWrapper.java
Index: helper/servlet/DefaultServletRequestWrapper.java
===================================================================
--- helper/servlet/DefaultServletRequestWrapper.java (revision 76)
+++ helper/servlet/DefaultServletRequestWrapper.java (working copy)
@@ -51,8 +51,6 @@
protected static final Logger logger =
Logger.getLogger(DefaultServletRequestWrapper.class.getName());
- public static String DEFAULT_ENCODING = "UTF-8"; //csc_041805_1
-
HttpServletRequest req = null;
List paramList = null;
String method = null;
@@ -150,7 +148,7 @@
//jrk_20030731.1_start - catch runtime exceptions in the decode()
method
try {
//csc_041805_1 queryString =
java.net.URLDecoder.decode(queryString);
- queryString =
java.net.URLDecoder.decode(queryString, DEFAULT_ENCODING);
//csc_041805_1
+ queryString =
java.net.URLDecoder.decode(queryString, getCharacterEncoding());
//ndc_041805_1
} catch (Exception e) {
logger.warn("failed to decode queryString, but
allowing to continue", e);
}
@@ -460,4 +458,4 @@
return it.next();
}
}
-}
\ No newline at end of file
+}
Christian Cryder wrote:
I have checked this change into place:
csc_041805_1 - Implemented support for configurable encoding.
1. added the following methods to BComponent
setEncoding(String enc), getEncoding(),
encodeStr(String s), decodeStr(String s)
2. calling getEncoding() will look to see if current
encoding is set, if not return
the parent encoding (which would iterate up the chain).
If there is no encoding specified,
BComponent.DEFAULT_ENCODING will be used. Note that the
encode/decodeStr methods are
protected, and they eat exceptions - if a problem is
encountered we try alternate encodings
or use the default.
3. BAction can now take advantage of this when encoding
params. Not sure if we need to make use
of this elsewhere as well.
4. To configure the default encodings, you can use the
object repository assembler to
set BComponent.DEFAULT_ENCODING or
DefaultServletRequestWrapper.DEFAULT_ENCODING
Denny, could you take a look and see if this works for you? Holler if
questions...
Christian
Christian Cryder wrote:
Ok, here's what I'd like to recommend -
1. I think we need to create a method in BComponent called
setEncoding(String enc)
2. calling getEncoding() would look to see if current encoding is set,
if not return the parent encoding (which would iterate up the chain).
If there is no encoding specified, "UTF-8" would be used. THis
basically gives us the ability to set the encoding for an entire
component structure, or just a subset.
3. we can then modify the renderers to take advantage of the encoding
by querying the components.
I think this should pretty much solve Denny's problem.
Now I have an additional question - searching the code to see where
else we reference encodings, I found this block of code in
DefaultServletRequestWrapper:
//csc_110102.1_start - fix deprecation issues
//csc_110402.1 - revert
//jrk_20030731.1_start - catch runtime exceptions in the decode() method
try {
queryString =
java.net.URLDecoder.decode(queryString);
} catch (Exception e) {
logger.warn("failed to decode queryString, but
allowing to continue", e);
}
//jrk_20030731.1_end
/*
try {
queryString =
java.net.URLDecoder.decode(queryString, "UTF-8");
} catch (UnsupportedEncodingException e) {
System.out.println("Encoding Exception: "+e);
e.printStackTrace();
}
*/
//csc_110102.1_end
What I'm trying to understand here is this - it looks like we
originally changed the code so as to specify UTF-8 (and thus not use
the deprecated method). Then it looks like Jake changed it back to the
default method (which is deprecated).
Does anyone remember why we did this???? I have a feeling there is a
reason, and I'm reluctant to just change it back without understanding
the implications...
THanks,
Christian
Denny Chambers wrote:
Hi
ALL
I have run across a problem in the BAction class. In my Barracuda
webapp, I am sending all data to the browser as UTF-8, however my local
Operating System which is running my webapp server may be in different
character encoding formats, such as UTF-8, CP1252, ISO-8859-1, etc. Now
when my OS is in UTF-8, I don't have any real problem sending extended
characters (Characters outside of the ASCII range, i.e. greater than
decimal 127). But when I am in a different code page like CP1252, I run
into a problem sending extended characters in URLs created by
BLink/BAction. In the method BAction.getAction(...) the parameter list
is passed to the java.net.URLEncoder.encode(String s). For one, this
method had been deprecated in favor of the
java.net.URLEncoder.encode(String s, String enc) method which allows
you to encode the String based on a particular character encoding
format. Since Barracuda is using the older encode method, which takes
no encoding parameter, the encoding is being done based on the JVM
default encoding, which is set as a system parameter "file.encoding".
This means the non-ASCII characters in my URL parameters are getting
coded using the byte sequence from the default OS character encoding,
instead of the UTF-8 character encoding I want. It looks like the
BAction classes need to allow for an encoding parameter to be
specified, so that URL encoding is done properly.
FYI - The W3C recommends that character encoding be done based on a
UTF-8 character set - http://www.w3.org/TR/html40/appendix/notes.html#non-ascii-chars
Thanks,
--
Foo bar rama
Christian
----------
Christian Cryder
Internet Architect, ATMReports.com
----------
"Coffee? I could quit anytime, just not
today"
http://seelifedifferently.blogspot.com
--
Foo bar rama
Christian
----------
Christian Cryder
Internet Architect, ATMReports.com
----------
"Coffee? I could quit anytime, just not
today"
http://seelifedifferently.blogspot.com
--
=========================================================
Denny Chambers
Unix IS user friendly - it's just picky about it's friends.
DefaultServletRequestWrapper.java
(text/plain, 16.2 KB)
/* * Copyright (C) 2003 Christian Cryder [[email protected]] * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * $Id: DefaultServletRequestWrapper.java,v 1.3 2004/08/14 18:14:15 jkjome Exp $ */ package org.barracudamvc.core.helper.servlet; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import org.apache.log4j.Logger; import org.barracudamvc.core.helper.state.ParamPersister; import org.barracudamvc.plankton.data.Param; //csc_010404_1 - revamped to extend from servlet package's HttpServletRequestWrapper /** * <p>This class acts as a thin wrapper around a ServletRequest. Most calls * are simply passed through to the underlying request object. This object * does however, expose a method which allows you to set parameters in * the request object. This was necessary for cases where we needed to * be able to do a POST, save the parameters somewhere, and then do a GET * and reconstitute the parameters from that. * * <p>When you instantiate this object, it will automatically check the * clients session to see if there are any parameter state information that * needs to be reconstituted into the current request. */ public class DefaultServletRequestWrapper extends HttpServletRequestWrapper implements BarracudaServletRequestWrapper { protected static final Logger logger = Logger.getLogger(DefaultServletRequestWrapper.class.getName()); HttpServletRequest req = null; List paramList = null; String method = null; /** * Create a DefaultServletRequestWrapper around some other * HttpServletRequest impl. The wrapper adds the ability to * add/remove parameter values. * * @param ireq the underlying HttpServletRequest */ public DefaultServletRequestWrapper(HttpServletRequest ireq) { super(ireq); setRequest(ireq); } //--------------- BarracudaServletRequestWrapper ------------- /** * Set the underlying request method. * * @param method the underlying request method (GET, POST, etc) */ public void setMethod(String imethod) { method = imethod; } /** * Get the underlying request method. */ public String getMethod() { return (method!=null ? method : req.getMethod()); } /** * Set a given parameter (note that this is backed by a hashmap, * so the structure is slightly different than that of the * underlying ServletRequest which allows multiple paramters * with the same name). This means that if you attempt to * set a parameter whose key already exists you will effectively * overwrite the existing value. * * @param name the key name for the parameter * @param value the value associated with the given key */ public void addParameter(String name, String value) { //eliminate the obvious if (name==null) return; //make sure the paramList is initialized if (paramList==null) setupParamList(); //finally store the new value paramList.add(new Param(name, value)); } /** * Returns the value of a request parameter as a String, or * null if the parameter does not exist. * * @param name the key name for the parameter * @return the parameter value associated with a key name */ public String getParameter(String name) { //eliminate the obvious if (name==null) return null; //if paramList exists, get the value from there if (paramList!=null) { Iterator it = paramList.iterator(); while (it.hasNext()) { Param param = (Param) it.next(); if (param.getKey().equals(name)) return param.getValue(); } return null; //otherwise just delegate to the underlying request } else { //merg_092901.1_start //This patch submitted by Merg [[email protected]]. The basic problem //is that some servlet containers do not accurately report all //parameters submitted with the request. If the request is a post, //and there were additional parameters submitted in the url, the //additional params are sometimes not returned (ie. on ATG Dynamo). //SO...if the value comes back null, then actually check the //query string for them. // return req.getParameter(name); String s = req.getParameter(name); if (s==null) { // Still no parameter found, check the queryString String queryString = req.getQueryString(); if (queryString!=null) { //csc_110102.1_start - fix deprecation issues //csc_110402.1 - revert //jrk_20030731.1_start - catch runtime exceptions in the decode() method try { //csc_041805_1 queryString = java.net.URLDecoder.decode(queryString); queryString = java.net.URLDecoder.decode(queryString, getCharacterEncoding()); //ndc_041805_1 } catch (Exception e) { logger.warn("failed to decode queryString, but allowing to continue", e); } //jrk_20030731.1_end /* try { queryString = java.net.URLDecoder.decode(queryString, "UTF-8"); } catch (UnsupportedEncodingException e) { System.out.println("Encoding Exception: "+e); e.printStackTrace(); } */ //csc_110102.1_end //jrk_20030828.1_start // int startPos = queryString.indexOf(name + "="); //need "=" to know it is a parameter name as opposed to a value // int endPos = -1; //need "=" to know it is a parameter name as opposed to a value // also need '&' to make difference between id and aaa_id for example // if is first param, will be checked in different place int startPos = queryString.indexOf("&"+name + "="); int endPos = -1; if (startPos == -1) { // not found inside parameter list, try find as first if (queryString.startsWith(name+"=")) { startPos = 0; } } else { // skip '&' startPos++; } //jrk_20030828.1_end if (startPos!=-1) { startPos = startPos + name.length() + 1; endPos = queryString.indexOf("&", startPos); if (endPos==-1) { s = queryString.substring(startPos); } else { s = queryString.substring(startPos, endPos); } } } } return s; //merg_092901.1_end } } /** * Returns an Enumeration of String objects containing the * names of the parameters contained in this request. * * @return an Enumeration of all the parameter names */ public Enumeration getParameterNames() { //if paramList is not null, get the enum from there if (paramList!=null) { return new LocalEnumerator(paramList); //otherwise just delegate to the underlying request } else { return req.getParameterNames(); } } /** * Returns a java.util.Map of the parameters of this request. * Request parameters are extra information sent with the request. * For HTTP servlets, parameters are contained in the query string * or posted form data. */ public Map getParameterMap() { //if paramList exists, get the value from there if (paramList!=null) { Iterator it = paramList.iterator(); Map paramMap = new HashMap(paramList.size()); // Map paramMap = new TreeMap(); //csc_120602.1_start /* Ok, so the problem with this is that a) the values should be stored as a String[], and b) if you have multiple values for a given its not going to work as implemented while (it.hasNext()) { Param param = (Param) it.next(); paramMap.put(param.getKey(), param.getValue()); */ //populate the paramMap with key/val pairs while (it.hasNext()) { Param param = (Param) it.next(); String key = param.getKey(); List valList = (List) paramMap.get(key); if (valList==null) { valList = new ArrayList(10); paramMap.put(key, valList); } valList.add(param.getValue()); } //now run back through the paramMap and convert all the //List values into String[] (to conform with servlet spec) it = paramMap.keySet().iterator(); while (it.hasNext()) { Object key = it.next(); List valList = (List) paramMap.get(key); //csc_010404_1_start //Not sure why, but all of a sudden this code was erring with a Class cast exception - //List seems to return an Object[] and that can't be cast to a String[] even if all the items //in the list are Strings // paramMap.put(key, (String[]) valList.toArray()); int idx = -1; String[] valArr = new String[valList.size()]; Iterator it2 = valList.iterator(); while (it2.hasNext()) { valArr[++idx] = (String) it2.next(); } paramMap.put(key, valArr); //csc_010404_1_end } //csc_120602.1_end return paramMap; //otherwise just delegate to the underlying request } else { return req.getParameterMap(); } } //csc_013102.1_end /** * Returns an array of String objects containing all of the * values the given request parameter has, or null if the * parameter does not exist. * * @param name the key name for the parameter * @return an array of Strings for the given key name */ public String[] getParameterValues(String name) { //eliminate the obvious if (name==null) return null; //if paramList is not null, build the array from there if (paramList!=null) { List valueList = new ArrayList(paramList.size()); Iterator it = paramList.iterator(); while (it.hasNext()) { Param param = (Param) it.next(); if (param.getKey().equals(name)) valueList.add(param.getValue()); } int idx = -1; String[] valueArr = new String[valueList.size()]; it = valueList.iterator(); while (it.hasNext()) { valueArr[++idx] = (String) it.next(); } if (valueArr.length==0) return null; else return valueArr; //otherwise just delegate to the underlying request } else { return req.getParameterValues(name); } } /** * Remove the first parameter whose key matches the specified name * * @param name the key name for the parameter */ public void removeParameter(String name) { //eliminate the obvious if (name==null) return; //make sure the paramList is initialized if (paramList==null) setupParamList(); //finally remove the first occurence of the parameter for (int i=0, max=paramList.size(); i<max; i++) { Param param = (Param) paramList.get(i); if (param.getKey().equals(name)) { paramList.remove(i); break; } } } /** * Remove all parameters for a specified name * * @param name the key name for the parameter */ public void removeAllParameters(String name) { //eliminate the obvious if (name==null) return; //make sure the paramList is initialized if (paramList==null) setupParamList(); //finally remove the all occurences of the parameter for (int i=paramList.size()-1; i>=0; i--) { Param param = (Param) paramList.get(i); if (param.getKey().equals(name)) paramList.remove(i); } } /** * Reset the parameter values to their original state * (ie. the actual values in the request) */ public void resetParameters() { paramList=null; } /** * Get the underlying servlet request. The only reason you * should ever have to do this is if you are trying to forward * a request. Some containers check to make sure that the * request object being forwarded is an instance of their own * implementation... * * @return the underlying servlet request object */ public HttpServletRequest getCoreRequest() { return req; } //--------------- ServletRequestWrapper ---------------------- /** * Set the underlying request object. * * @param ireq the underlying HttpServletRequest */ public void setRequest(ServletRequest ireq) { super.setRequest(ireq); if (req!=ireq && ireq instanceof HttpServletRequest) { //set the reference to the req req = (HttpServletRequest) ireq; //reconstitute any param values from the user's session ParamPersister.reconstituteReqParamState(this); } } //--------------- HttpServletRequestWrapper ------------------ //-------------------- Utility stuff ------------------------- private void setupParamList() { //eliminate the obvious (only initialize once!) if (paramList!=null) return; //create the param list paramList = new ArrayList(10); //now copy in all param values from the underlying servlet //request. From this point on then, the param values will //be maintained in the paramList Enumeration enumeration = req.getParameterNames(); while (enumeration.hasMoreElements()) { //get the key String key = (String) enumeration.nextElement(); //find all values associated with the key String[] vals = req.getParameterValues(key); for (int i=0, max=vals.length; i<max; i++) { paramList.add(new Param(key, vals[i])); } } } /** * This inner class implements Enumaration. It will effectively * enumerate over all of the parameter key names. */ class LocalEnumerator implements Enumeration { List keyList = null; Iterator it = null; public LocalEnumerator(List iparamList) { keyList = new ArrayList(iparamList.size()); it = iparamList.iterator(); while (it.hasNext()) { Param param = (Param) it.next(); if (!keyList.contains(param.getKey())) keyList.add(param.getKey()); } it = keyList.iterator(); } public boolean hasMoreElements() { return (it.hasNext()); } public Object nextElement() { return it.next(); } } }
BComponent.java
(text/plain, 24.5 KB)
/* * Copyright (C) 2003 Christian Cryder [[email protected]] * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * $Id: BComponent.java,v 1.39 2004/08/14 18:14:14 jkjome Exp $ */ package org.barracudamvc.core.comp; import java.io.*; import java.net.*; import java.util.*; import org.apache.log4j.Logger; import org.barracudamvc.core.comp.renderer.RenderStrategy; import org.barracudamvc.core.comp.renderer.Renderer; import org.barracudamvc.core.comp.renderer.RendererFactory; import org.barracudamvc.core.comp.renderer.html.HTMLComponentRenderer; import org.barracudamvc.core.comp.renderer.xml.XMLComponentRenderer; import org.w3c.dom.DOMException; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.html.HTMLDocument; import org.w3c.dom.html.HTMLElement; /** * Defines the base component class from which all other Barracuda * components are derived. Its Swing counterpart would be JComponent. */ public class BComponent extends AbstractBComponent { //public constants protected static final Logger logger = Logger.getLogger(BComponent.class.getName()); //constants public static final String VISIBILITY_MARKER = "visdom"; public static String DEFAULT_ENCODING = System.getProperty("file.encoding"); //ndc_041805_1 //private vars //NOTE: the name must remain null unless specifically set; setting the name //will cause the name attribute to be set in the markup to which the component //is bound (meaning, if the markup specifies a form name that will get overwritten //if you specify a name) // protected String name = this.getClass().getName(); protected String name = null; protected boolean visible = true; protected boolean enabled = true; //072304_1 - moved to AbstractBComponent //072304_1 protected Map attrs = null; protected RenderStrategy rs = null; //csc_110201.1 protected String enc = null; //csc_041805_1 /** * @clientCardinality 0..* */ private View lnkView; //--------------- Renderer ----------------------------------- /** * Default component renderer factory registrations */ static { HTMLRendererFactory rfHTML = new HTMLRendererFactory(); installRendererFactory(rfHTML, BComponent.class, HTMLElement.class); installRendererFactory(rfHTML, BComponent.class, HTMLDocument.class); XMLRendererFactory rfXML = new XMLRendererFactory(); installRendererFactory(rfXML, BComponent.class, Node.class); } /** * HTML RendererFactory */ static class HTMLRendererFactory implements RendererFactory { public Renderer getInstance() {return new HTMLComponentRenderer();} } /** * XML RendererFactory */ static class XMLRendererFactory implements RendererFactory { public Renderer getInstance() {return new XMLComponentRenderer();} } //--------------- BComponent --------------------------------- /** * Set the name for this component. * * <p>Note that for several types of views (HTMLAnchorElement, HTMLAppletElement, * HTMLButtonElement, HTMLFormElement, HTMLFrameElement, HTMLIFrameElement, * HTMLInputElement, HTMLMapElement, HTMLMetaElement, HTMLObjectElement, * HTMLParamElement, HTMLSelectElement, and HTMLTextAreaElement) this property * will be used in rendering if it is actually set. This means that if you set * the component name, and it is bound to a view that is backed by one of these * nodes, then the name attribute in that node will be overridden. So be careful!!! * * @param iname the name for this component */ public BContainer setName(String iname) { name = iname; return this; } /** * Get the name for this component * * @return the name for this component */ public String getName() { return name; } /** * Set the component visibility * * @param val true if the component should be visible */ public BComponent setVisible(boolean val) { return setVisible(val, false); } /** * Set the component visibility recursively * * @param val true if the component should be visible * @param recurse true if we want to set this value recursively */ public BComponent setVisible(boolean val, boolean recurse) { visible = val; if (recurse) { Iterator it = children.iterator(); while (it.hasNext()) { BContainer child = (BContainer) it.next(); if (child!=null && child instanceof BComponent) { BComponent wcomp = (BComponent) child; wcomp.setVisible(val, recurse); } } } invalidate(); return this; } /** * Get the component visibility * * @return true if the component is visible */ public boolean isVisible() { return visible; } /** * Enable/disable the component * * @param val true if the component should be enabled */ public BComponent setEnabled(boolean val) { return setEnabled(val, false); } /** * Enable/disable the component recursively * * @param val true if the component should be enabled * @param recurse true if we want to set this value recursively */ public BComponent setEnabled(boolean val, boolean recurse) { enabled = val; if (recurse) { Iterator it = children.iterator(); while (it.hasNext()) { BContainer child = (BContainer) it.next(); if (child!=null && child instanceof BComponent) { BComponent wcomp = (BComponent) child; wcomp.setEnabled(val, recurse); } } } invalidate(); return this; } /** * See if the component is enabled * * @return true if the component is enabled */ public boolean isEnabled() { return enabled; } //csc_041805_1 - added /** * Set the encoding for this component (and all children, unless they specifically * specify a different encoding). Defaults to DEFAULT_ENCODING * * @param enc the encoding for this component (null = look to parent) */ public void setEncoding(String ienc) { enc = ienc; } //csc_041805_1 - added /** * Get the encoding for this component. If null, look to parent. If parent is null, default * to DEFAULT_ENCODING * * @return the encoding for this component */ public String getEncoding() { if (enc!=null) return enc; else if (parent!=null && parent instanceof BComponent) return ((BComponent) parent).getEncoding(); else return DEFAULT_ENCODING; } //csc_041805_1 - added /** * Actually encode a string based on the current encoding - note that we * eat any acceptions that get thrown; if they occur we simply re-encode using * UTF-8 */ protected String encodeStr(String s) { String s2 = s; try { s2 = URLEncoder.encode(s, getEncoding()); } catch (UnsupportedEncodingException e) { try { s2 = URLEncoder.encode(s, DEFAULT_ENCODING); } catch (UnsupportedEncodingException e2) { try { s2 = URLEncoder.encode(s, "UTF-8"); } catch (UnsupportedEncodingException e3) { //if THAT doesn't work we're just going to return the string that came in } } } return s2; } //csc_041805_1 - added /** * Actually decode a string based on the current encoding - note that we * eat any acceptions that get thrown; if they occur we simply re-encode using * UTF-8 */ protected String decodeStr(String s) { String s2 = s; try { s2 = URLDecoder.decode(s, getEncoding()); } catch (UnsupportedEncodingException e) { try { s2 = URLDecoder.decode(s, DEFAULT_ENCODING); } catch (UnsupportedEncodingException e2) { try { s2 = URLDecoder.decode(s, "UTF-8"); } catch (UnsupportedEncodingException e3) { //if THAT doesn't work we're just going to return the string that came in } } } return s2; } /** * Set the components primary view. This method effectively * removes any other views and binds the component to the * newly specified view. * * @param view the view to which this component is bound */ public BComponent setView(View view) { removeAllViews(); return addView(view); } /** * Bind a component to a view * * @param view the view to which this component is bound */ public BComponent addView(View view) { // dbr_20020415.1_start if (view==null || (views!=null && views.contains(view))) return this; if (views==null) { views = new ArrayList(); } // dbr_20020415.1_end views.add(view); invalidate(); return this; } /** * Remove a view from this component * * @return true if we were able to remove the view from the component */ public boolean removeView(View view) { if (view==null) return false; invalidate(); // dbr_20020415.2 return (views!=null && views.remove(view)); } /** * Remove all views from this component */ public BComponent removeAllViews() { if (views!=null) views.clear(); invalidate(); return this; } /** * Get a list of all the views for this component. This returns * a copy of the underlying view list. * * @return a List of all the views for this component */ public List getViews() { //jrk_20020414.1_start //make sure we don't send a null views object to the //ArrayList constructor or we'll get a NullPointerException //just return null if views is null //Note: views only seems to be null when the Barracuda libraries are in //the servlet container's common webapp lib directory (eg.. $TOMCAT_HOME/lib) //and not when they exist in the local webapp's WEB-INF/lib directory. //Why is this???? Probably should find the root cause. Likely has to do with //more classloader problems... //see if we have any views if (views!=null) { return new ArrayList(views); } return null; //jrk_20020414.1_end } protected View getFirstView() { //see if we have any views if (views!=null && views.size()>0) { return (View) views.get(0); } //if not, see if the children have any views View firstView = null; if (children!=null) { Iterator it = children.iterator(); while (it.hasNext()) { Object o = it.next(); if (o instanceof BComponent) { firstView = ((BComponent) o).getFirstView(); if (firstView!=null) break; } } } return firstView; } /** * set an attribute for this particular component. When the component * is rendered, component attributes will be shown as element attributes * in the elements that back each of the views associated with this component. * This means that if you set an attribute for the component, it will * affect all views associated with the component.If you wish to set an * attribute for a specific view alone, then you should get the view, find * the node that backs it, and then set the attribute manually that way. * * @param attr the attribute name * @param val the attribute value */ //csc_072604_1 - moved to AbstractBComponent /* public void setAttr(Object attr, Object val) { if (attrs==null) attrs = new TreeMap(); attrs.put(attr,val); invalidate(); } */ /** * get an attribute associated with this particular component. Note that * the attribute map that backs this method only keeps tracks of specific * attributes you have added to the component. It does not look at attributes * that are physically associated with the underlying elements that back each * of the views associated with this component. What this means is that if * the template that backs a view has some attribute "foo" and you try to * see the value of that attribute using this method, you will not be able * to find it unless you have actually associated an attribute named "foo" * with the specific component. * * @param attr the attribute name * @return the value for the given attribute (may be null) */ //csc_072604_1 - moved to AbstractBComponent /* public Object getAttr(Object attr) { if (attrs==null) return null; return attrs.get(attr); } */ /** * get a copy of the underlying component attribute Map * * @return a copy of the underlying component attribute Map */ //csc_072604_1 - moved to AbstractBComponent /* public Map getAttrMap() { if (attrs==null) return null; return new TreeMap(attrs); } */ //csc_110201.1 - added /** * Set the render strategy for this component and all its children. * Valid values include: * <ul> * <li>RenderStrategy.SCRIPT_AS_NEEDED</li> * <li>RenderStrategy.NEVER_SCRIPT</li> * <li>RenderStrategy.CUSTOM_SCRIPT</li> * <li>null</li> * <ul> * * <p>If the value is null, it will inherit setting from parent. If the * setting for the root parent is null, it will default to * RenderStrategy.DEFAULT_RENDER_STRATEGY * * @param irs the RenderStrategy */ public BComponent setRenderStrategy(RenderStrategy irs) { rs = irs; return this; } //csc_110201.1 - added /** * Get the render strategy for this component * * @return the render strategy for this component */ public RenderStrategy getRenderStrategy() { if (rs!=null) return rs; else if (parent==null) return RenderStrategy.DEFAULT_RENDER_STRATEGY; else if (parent instanceof BComponent) return ((BComponent) parent).getRenderStrategy(); else return RenderStrategy.DEFAULT_RENDER_STRATEGY; } /** * Render the component for a view with the specified * ViewContext. You shouldn't override this method unless * you really know what you're doing... * * @param vc ViewContext for the client view * @throws RenderException if the particular View cannot be rendered */ public BComponent render(ViewContext vc) throws RenderException { render(vc, 0); return this; } protected void render(ViewContext vc, int depth) throws RenderException { //if the component is not already validated, re-render it Iterator it = null; if (!validated) { try { //prepare for rendering if (logger.isInfoEnabled()) logger.info("preparing to render comp "+this.toRef()); // preRender(vc, 0); preRender(vc, depth); //add in any temp views if (logger.isInfoEnabled()) logger.info("adding in temp views "+this.toRef()); if (tempViews!=null) { //jrk_20021018.1 - added null check for views if (views==null) views = new ArrayList(); views.addAll(tempViews); } //render our own views if (logger.isDebugEnabled()) logger.debug("rendering our own view in comp "+this.toRef()); boolean hasView = false; View view = null; it = views.iterator(); while (it.hasNext()) { view = (View) it.next(); if (view==null) continue; hasView = true; try { if (logger.isDebugEnabled()) logger.debug("view is bound to node: "+view.getNode()); if (view.getNode() instanceof Element) { Element el = (Element) view.getNode(); //set visibility if (isVisible()) { el.removeAttribute(VISIBILITY_MARKER); } else { el.setAttribute(VISIBILITY_MARKER, "false"); return; //if the component is not visible, there is no need to go any further!!! } //csc_072604_1 - I'm not really sure why we ever decided to render the attributes here, //as opposed to allowing it to happen in the renderers. I am moving that logic there now... /* //set any attributes if (attrs!=null) { Iterator it2 = attrs.keySet().iterator(); while (it2.hasNext()) { Object key = it2.next(); Object val = attrs.get(key); // Begin ndc_073002.1 - I noticed a problem where I was getting two // attribute of the same name in my html output. In my template html // lets say I had something like this: <img src="foo.png" alt="foo image">. // In my model I had some code like this: // BComponent img = new BComponent(); // img.setAttr(" alt", "foobar"); // return img; // // The resulting html would look like this: // <img src="foo.png" alt="foo image" alt="foobar"> // // Because of the extra space in " alt" it does not match this // attribute with the attribute in the html template. So instead of // overwriting the attribute, it adds the attribute. I am adding a // trim() to the key.toString() instructions below. if (val==null) { el.removeAttribute(key.toString().trim()); } else { el.setAttribute(key.toString().trim(), val.toString()); } // End ndc_073002.1 } } */ } //render the view //csc_012605_1 vc.putState(ViewContext.ELEMENT_FACTORY, view.getElementFactory()); renderView(view, vc, depth+1); } catch (RenderException e) { //for now just consume the exceptions logger.warn("RenderException:"+e+" for View:"+view, e); } } // if (!hasView) logger.warn("component not bound to any views! (may be a sign that something's wrong?)"); //now invoke render in all of our children if (logger.isDebugEnabled()) logger.debug("rendering our children in comp "+this.toRef()+(this.hasChildren() ? "" : "(n/a)")); BContainer child = null; it = children.iterator(); while (it.hasNext()) { child = (BContainer) it.next(); if (child==null) continue; try { if (child instanceof BComponent) { BComponent wcomp = (BComponent) child; if (wcomp.supports(vc)) { wcomp.render(vc, depth+1); } } } catch (RenderException e) { //for now just consume the exceptions logger.warn("RenderException:"+e+" for Child:"+child, e); } } } finally { //allow for any cleanup after render if (logger.isDebugEnabled()) logger.debug("cleaning up after render in comp "+this.toRef()+(this.hasChildren() ? "" : "(n/a)")); // postRender(vc, 0); postRender(vc, depth); //cleanup any step children removeAllStepChildren(); //cleanup any temp views if (tempViews!=null) if (tempViews!=null) { it = tempViews.iterator(); while (it.hasNext()) { View tempView = (View) it.next(); this.removeView(tempView); } tempViews = null; } //now consider ourselves validated validated = true; } } if (logger.isInfoEnabled()) logger.info("rendering complete in comp "+this.toRef()); } /** * Prep phase before rendering. This is typically where you would put * any pre-rendering specific logic. */ // protected final void preRender(ViewContext vc) { // preRender(vc, 0); // } protected void preRender(ViewContext vc, int depth) { //this is actually the method you should override when implementing //pre-render logic //(--n/a--) } /** * Render a specific view for the component. * * @param view View to be rendered * @param vc ViewContext for the client view * @throws RenderException if the particular View cannot be rendered */ protected final void renderView (View view, ViewContext vc) throws RenderException { renderView(view, vc, 0); } protected void renderView (View view, ViewContext vc, int depth) throws RenderException { //021102.3_csc_start - this method didn't used to do anything, which meant that //if you tried to use a plain-jane BComponent to control visibility it would never //actually get rendered. Dumb. THis should make it work now... if (logger.isInfoEnabled()) logger.info("rendering view: "+view); //actually render the view according to known interfaces try { Renderer r = getRenderer(view); r.renderComponent(this, view, vc); } catch (DOMException e) { logger.warn("DOM Error:", e); throw new DOMAccessException("Error rendering component in view:"+e, e); } //021102.3_csc_end } /** * Cleanup after rendering. This method is guaranteed to be invoked, even * if there is an error during rendering. This is typically where you would * put any custom post-rendering cleanup. */ // protected final void postRender(ViewContext vc) { // postRender(vc, 0); // } protected void postRender(ViewContext vc, int depth) { //this is actually the method you should override when implementing //post-render logic //(--n/a--) } /** * Determine if a specific ViewContext is supported * by this component * * @param vc ViewContext for the client view * @return true if the specified ViewCapabilites are supported */ public boolean supports(ViewContext vc) { return true; } }
smime.p7s
(application/x-pkcs7-signature, 3.1 KB) - not displayed