Return-Path: Delivered-To: apmail-jakarta-commons-dev-archive@apache.org Received: (qmail 93268 invoked from network); 28 Dec 2001 14:40:44 -0000 Received: from unknown (HELO nagoya.betaversion.org) (192.18.49.131) by daedalus.apache.org with SMTP; 28 Dec 2001 14:40:44 -0000 Received: (qmail 4157 invoked by uid 97); 28 Dec 2001 14:40:41 -0000 Delivered-To: qmlist-jakarta-archive-commons-dev@jakarta.apache.org Received: (qmail 4141 invoked by uid 97); 28 Dec 2001 14:40:41 -0000 Mailing-List: contact commons-dev-help@jakarta.apache.org; run by ezmlm Precedence: bulk List-Unsubscribe: List-Subscribe: List-Help: List-Post: List-Id: "Jakarta Commons Developers List" Reply-To: "Jakarta Commons Developers List" Delivered-To: mailing list commons-dev@jakarta.apache.org Received: (qmail 4130 invoked from network); 28 Dec 2001 14:40:40 -0000 Message-ID: From: Rey Francois To: 'Jakarta Commons Developers List' Subject: [Design Discussion] DynaBeans Converters Date: Fri, 28 Dec 2001 14:44:56 +0100 MIME-Version: 1.0 X-Mailer: Internet Mail Service (5.5.2653.19) Content-Type: text/plain; charset="iso-8859-1" X-Spam-Rating: daedalus.apache.org 1.6.2 0/1000/N X-Spam-Rating: daedalus.apache.org 1.6.2 0/1000/N In my previous posts I thought there was a Converter per property, effectively resulting in multiple Converters associated to one DynaBean. I understand now I was wrong and the Converter you both (Craig + Paulo) talk about is more like this: - there is only *one* Converter assigned to one DynaBean instance, or none. - Converters are responsible for converting a given object value to a specific type, meaning to an instance of a given Class. - the same convert method is called for all conversion, that is to say: all properties of the same DynaBean that need conversion will use the same converter method (the one defined in the interface) on the same Converter instance. If this new understanding is right -please correct me if I still miss the point- then I'm still not comfortable about this design. Let me give you an example taken from a real-life project, although simplified for the purpose of the discussion. It's about a system for managing orders of financial funds. Here is the value object that I receive from the backend when displaying an order: public class OrderData { public String getType(); // return "B" for "Buy" and "S" for "Sell" public BigInteger getQuantity(); public BigDecimal getUnitPrice(); ... } If want to display this order in Struts, using your design I would create a DynaActionForm that stores Strings using the following Converter in the set methods: public OrderConverter implements Converter { public Object convert(Class type, Object value) { if (Class!=String.class) throw SomeException; Object result = null; if (value instanceof String) { result = value; } else if (value instanceof BigDecimal) { result = // convert BigDecimal to String } else if (value instanceof BigInteger) { result = // convert BigInteger to String } return result; } } The set method in my DynaActionForm would be like this: public void set(String name, Object value) { setInternal(name, getConverter().convert(String.class, value)); } So far so good, I see some value in this approach. But what if I don't want to display "B" but "Buy" for the type? I could write this in the convert method: if (value instanceof String) { if "B".equals(value) result = "Buy"; .... That would work until ... I have another property of type String that I wand to convert as well: public class OrderData { // new property: public String getStateId(); // returns "O" for "Open", "M" for "matched", "R" for "Waiting for repair", etc. // Below is same as before: public String getType(); // return "B" for "Buy" and "S" for "Sell" public BigInteger getQuantity(); public BigDecimal getUnitPrice(); ... } Unless there is no overlap between type codes and state codes, I can survive, but that's not a desirable situation. So before going any further in this discussion, is my understanding of Converters correct ? Fr. -----Original Message----- From: Craig R. McClanahan [mailto:craigmcc@apache.org] Sent: 27 December 2001 22:20 To: Jakarta Commons Developers List; paulo.gaspar@krankikom.de Subject: RE: [Design Discussion] DynaBeans - Round 2 On Thu, 27 Dec 2001, Paulo Gaspar wrote: > > From: Rey Francois [mailto:Francois.Rey@capco.com] > > > > I had to crate a similar interface in a data mapper framework I developed, > > however in the signature of the conversion method I included an additional > > parameter for the Locale. Without it, support for internationalized > > conversions is difficult. > > The method I use is to have a different converter for each Locale where such > distinction is necessary (which happens quite often). > > Notice that I have the option of assigning a converter per DynaBean and not > per DynaClass as Craig wrote in his proposal exactly because of situations > like this. The converter often needs some kind of contextual information. > I'm planning on ripping the getConverter() back out of DynaClass -- it's too restrictive. Paulo, did you ever run into a scenario where you had to be accessing the same Record with different Converters at the same time ... perhaps even on separate threads? This would cause some grief with the idea of assigning a Converter to a DynaBean instance. > This situations also made me implement my converters in a hierarchical way, > meaning that converter may just add or override a few conversion rules to a > "parent" converter. Some of those conversion rules may add context > information like "Locale" or "Base directory". > That makes a lot of sense. > > Perhaps this could be added in a new method of the > > Converter interface, or perhaps it can be included in another interface > > extending the Converter, e.g. LocalizedConverter extends Converter. > > You only need to place that kind of thing in the implementation. The > interface can stay as it is. That's certainly obvious in retrospect :-). I like Converter-per-Locale (or for whatever extra context information it needs). I'm still thinking about explicitly tying converters to DynaBean instances ... Paulo, do you normally design your Converters to go both directions (i.e. so you can use them in the getters *and* the setters), or just one way? In the latter case, you'd probably need two. > > > What do you think? > > > > Actually the more I think about this, the more I'm wondering if it makes > > sense to include a conversion feature into DynaBeans. My gut feeling would > > be to restrict DynaBeans to be only dynamic data containers, and handle > > conversions separately. > > Notice that my DynaBean (actually called IRecord) interface does not know > anything about converters - only my most used implementation does so. > Ah ... so you really could use two one-way Converters if you wanted to ... but the key is that support for conversion is a feature of a particular DynaBean implementation class -- not of the top-level interface. I'd like to keep the top-level interfaces as simple as possible, to allow for variations in the implementations underneath. > I found that I am converting types most of the time, like when I read to the > DynaBean things like: > - Servlet parameters; > - ResultSet columns; > - XML attributes; > - A big etc. > > Using a helper class instead of a simple set() method all the time becomes a > pain in the ass, especially because it would involve also getting property > type information. So, you would need something like: > > DynaClass myBeanClass = myBean.getDynaClass(); > PropertyDescriptor propDesc = > myBeanClass.getPropertyDescriptor("myprop"); > myBean.set("myprop", cvt.convert(propDesc.getPropertyType(), newValue)); > > instead of > myBean.set("myprop", newValue); > > Notice also that: > - You have the opportunity to make a much more efficient implementation > inside the DynaBean; > - The DynaBean implementation can default (as mine) to the use of a > NoOpConverter that performs no conversion at all. > > > > First not all situations require the use of conversions. > > The above NoOpConverter solves that. And notice that I am talking about an > implementation - I agree that the converter should be kept away from the > interface. > You could of course also implement a DynaBean base class that doesn't support the conversion functionality at all for cases where it's not needed. > > Secondly if conversions need to be done, I'd probably handle it > > separately from the original bean. Most of the time conversions are > > necessary because of a specific usage situation requiring a different and > > specific format, and you cannot or do not want to change the > > original format > > to accommodate this new usage situation (either because you do not own > > the original format specification, or because you don't want to make > > it specific and prefer to keep it generic and reusable). > > Supposing we have an EJB backend that provides a value object. Should the > > value object be coded to support String conversions for GUI display? You > > can do this, but that means all your client have to follow the same > > formatting conventions. > > That is why, as above, the Converter should be DynaBean specific and not > DynaClass specific. > > > To avoid this you either convert on-the-fly (e.g. using some > > kind of wrapper), or you create another data container that stores the > > converted values. > > Or you assign a new Converter to the DynaBean. > > > Either way the conversion are handled outside the original > > bean itself. In the case of Struts we use a separate data container (there > > are good reasons for this), ActionForms, and conversions are either custom > > coded or handled by the ConvertUtils class. > > > If we had to use DynaBeans in Struts for implementing > > ActionForms, we could > > make a case for making these "DynActionForms" able to perform some > > conversions. We could implement transparently the use of converters in the > > set() method like Paulo Gaspar suggests in order to populate a > > DynActionForm > > from a value object (or business object). However we would probably need > > to do the reverse conversion in the get() method so that we can populate a > > value object with a DynActionForm. This would require some ways of passing > > the target type to the get() method so it can select the appropriate > > converter. > > It is too messy to have such a get() method. What I do is to wrap the value > object with another DynaBean implementation. > > I have a kind of DynaBeanWrapper class which also uses a Converter and that > can wrap any object with "properties" (or "fields" or "columns"). Only bits > of the DynaClass need to change depending of what kind of object you are > wrapping - the setter and getter implementations. > > Then there is a "DynaIntrospector" that examines the object to be wrapped > (e.g. a bean or a ResultSet) and builds an appropriate DynaClass. (For beans > such instrospection provides a nice place to hold an introspection cache.) > > Of course that you do not need such thing if you just want to convert all > the fields of a DynaBean into text. You might use a Converter for that too > but it is simpler to do it outside the bean, as in: > > String s = cvt.convert(java.lang.String.class, myDynaBean.get("myProp")); > > and the "cvt" Converter can hold the appropriate conversion rules to format > each data type. > > > I also have a converter helper tool for the "asThis" and "asThat" stuff. > Just to give you an idea, it is used like this: > > CvtHelper cvt = new CvtHelper(myConverter); > > int i = cvt.asInt(toIntValue, -1); > Integer io = cvt.asInteger(toIntValue); > > String s = cvt.asString(toStrValue); > > So, it is a class that allows you to specify a converter to be used to > perform the most frequent conversions to primitive and "java.lang" types. > > I think using such tool is much simpler and flexible that having the usual > getSomeType() helper methods hanging in every other class. And the > implementation is obvious. Like: > > public class CvtHelper > { > private final Converter m_cvt; > > public CvtHelper(final Converter i_cvt) > { m_cvt = i_cvt; > } > ... > > public final String asString(final Object i_value) > { > return (String)(m_cvt.convert(String.class, i_value)); > } > > ... > } > > > > Such design would also mean that we must easily differentiate > > between the various input/output types: it should be possible for > > a JSP page > > to ensure it gets an appropriate display value as a String, while on the > > other side it should be possible to get an appropriate "business" value. > > Using the same getter method for these two scenario seems to > > introduce some complications. > > As mentioned above (different converters, only one getter, etc.). > > > > What if the business value is already a String, which has to > > be properly converted to another String for display (e.g. > O<->Open)? > > In such case you cannot really use the Class as a discriminant between > > the different types, since both are Strings. Should we use two get methods > > instead? > > That only depends on the Converter implementation. Remember that Converter > is an interface. > > > > > Seems like an overkill... Also doing such conversion in an ActionForm is > > in some cases introducing some overlap with validation. > > It is complementary to Validation. For an input value to be valid it must > be possible to convert it to the destination type. > In my Struts-based designs, I tend to check for convertability in my ActionForm validate() methods -- conversion success is necessary but not sufficient for validity, so you're checking for other stuff there anyway. > > > Sometimes being able to successfully convert a display String to a > > business value is enough to validate it, at least partially: a String > > successfully parsed by SimpleDateFormat is a valid date, no need to > > validate each character, and on > > the resulting Date object further validation can be performed (is it a > > future date, etc.). > > So, this means that you also find out that it is a complementary issue. > > > > Finally add the internationalization issue I started with... > > Again, a converter per Locale and DynaBean specific converters. > > > > Ok, I will conclude here because my written thinking is getting > > too long ;) > > I suffer of the same "problem". > =;o) > > > > My conclusion would be to leave aside the conversion feature and > > focus only on the data container aspect. > > Containing the data is the simple bit. A Map would do. > > The usefulness from the DynaBean comes from it being able to help us > performing the most usual tasks with this kind of data with less > routine coding and with similar or improved quality (data validation > and other). > > > > Fr. > > Have fun, > Paulo Gaspar > Craig -- To unsubscribe, e-mail: For additional commands, e-mail: ************************************************************************ The information in this email is confidential and is intended solely for the addressee(s). Access to this email by anyone else is unauthorised. If you are not an intended recipient, please notify the sender of this email immediately. You should not copy, use or disseminate the information contained in the email. Any views expressed in this message are those of the individual sender, except where the sender specifically states them to be the views of Capco. http://www.capco.com *********************************************************************** -- To unsubscribe, e-mail: For additional commands, e-mail: