/*
* Copyright (c) 2001 - 2005 ivata limited.
* All rights reserved.
* -----------------------------------------------------------------------------
* ivata masks may be redistributed under the GNU General Public
* License as published by the Free Software Foundation;
* version 2 of the License.
*
* These programs are free software; you can redistribute them and/or
* modify them under the terms of the GNU General Public License
* as published by the Free Software Foundation; version 2 of the License.
*
* These programs are distributed in the hope that they will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License in the file LICENSE.txt for more
* details.
*
* If you would like a copy of the GNU General Public License write to
*
* Free Software Foundation, Inc.
* 59 Temple Place - Suite 330
* Boston, MA 02111-1307, USA.
*
*
* To arrange commercial support and licensing, contact ivata at
* http://www.ivata.com/contact.jsp
* -----------------------------------------------------------------------------
* $Log: DefaultFieldWriterFactory.java,v $
* Revision 1.15 2005/10/11 18:54:06 colinmacleod
* Fixed some checkstyle and javadoc issues.
*
* Revision 1.14 2005/10/03 10:17:25 colinmacleod
* Fixed some style and javadoc issues.
*
* Revision 1.13 2005/10/02 14:06:32 colinmacleod
* Added/improved log4j logging.
*
* Revision 1.12 2005/09/29 12:12:30 colinmacleod
* Added password field type.
*
* Revision 1.11 2005/09/16 13:41:18 colinmacleod
* Created new convertor class to handle timestamps.
*
* Revision 1.10 2005/09/14 12:54:31 colinmacleod
* Added checking for hidden fields of
* numeric or date types.
*
* Revision 1.9 2005/04/09 18:04:17 colinmacleod
* Changed copyright text to GPL v2 explicitly.
*
* Revision 1.8 2005/01/19 12:51:03 colinmacleod
* Changed Id --> Name.
*
* Revision 1.7 2005/01/10 19:05:12 colinmacleod
* Removed reflection and simplified override method
* for field writer classes.
*
* Revision 1.6 2005/01/07 08:08:24 colinmacleod
* Moved up a version number.
* Changed copyright notices to 2005.
* Updated the documentation:
* - started working on multiproject:site docu.
* - changed the logo.
* Added checkstyle and fixed LOADS of style issues.
* Added separate thirdparty subproject.
* Added struts (in web), util and webgui (in webtheme) from ivata op.
*
* Revision 1.5 2004/12/30 20:27:55 colinmacleod
* Added reflection to let you override the field writer classes used.
*
* Revision 1.4 2004/12/29 15:30:46 colinmacleod
* Added asserts to check parameters are not null.
*
* Revision 1.3 2004/11/12 15:10:41 colinmacleod
* Moved persistence classes from ivata op as a replacement for
* ValueObjectLocator.
*
* Revision 1.2 2004/11/11 13:44:04 colinmacleod
* Added HTMLFormatter.
*
* Revision 1.1.1.1 2004/05/16 20:40:32 colinmacleod
* Ready for 0.1 release
* -----------------------------------------------------------------------------
*/
package com.ivata.mask.web.field;
import org.apache.log4j.Logger;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import javax.servlet.http.HttpSession;
import org.apache.commons.beanutils.PropertyUtils;
import com.ivata.mask.field.Field;
import com.ivata.mask.field.FieldValueConvertor;
import com.ivata.mask.field.date.DateFieldValueConvertor;
import com.ivata.mask.field.date.TimestampFieldValueConvertor;
import com.ivata.mask.field.number.NumberFieldValueConvertor;
import com.ivata.mask.field.valueobject.ValueObjectFieldValueConvertor;
import com.ivata.mask.persistence.PersistenceManager;
import com.ivata.mask.persistence.PersistenceSession;
import com.ivata.mask.util.SystemException;
import com.ivata.mask.valueobject.ValueObject;
import com.ivata.mask.web.field.hidden.HiddenFieldWriter;
import com.ivata.mask.web.field.text.PasswordFieldWriter;
import com.ivata.mask.web.field.text.TextAreaFieldWriter;
import com.ivata.mask.web.field.text.TextFieldWriter;
import com.ivata.mask.web.field.valueobject.ValueObjectFieldWriter;
import com.ivata.mask.web.format.HTMLFormatter;
import com.ivata.mask.web.format.LineBreakFormat;
/**
* <p>
* Use this utility class to generate an appropriate field writer for a given
* mask and field.
* </p>
*
* @since ivata masks 0.1 (2004-05-14)
* @author Colin MacLeod
* <a href='mailto:colin.macleod@ivata.com'>colin.macleod@ivata.com</a>
* @version $Revision: 1.15 $
*/
public class DefaultFieldWriterFactory implements FieldWriterFactory {
/**
* Logger for this class.
*/
private static final Logger logger = Logger
.getLogger(DefaultFieldWriterFactory.class);
/**
* <copyDoc>Refer to {@link #getActionPage}.</copyDoc>
*/
private String actionPage;
/**
* <p>
* Formatter used to format all texts.
* </p>
*/
private HTMLFormatter formatter = new HTMLFormatter();
/**
* <p>
* This object is used to retrieve lists for collection field writers.
* </p>
*/
private PersistenceManager persistenceManager;
/**
* Construct a writer factory.
*
* @param persistenceManagerParam
* used to retrieve value objects for a value
* @param actionPageParam
* page of the action to which we'll link value objects to.
*/
public DefaultFieldWriterFactory(
final PersistenceManager persistenceManagerParam,
final String actionPageParam) {
this.persistenceManager = persistenceManagerParam;
this.actionPage = actionPageParam;
// for now, we only have one format in the formatter
LineBreakFormat lineBreakFormat = new LineBreakFormat();
lineBreakFormat.setConvertLineBreaks(true);
formatter.add(lineBreakFormat);
}
/**
* Page of the <strong>Struts </strong> action to which we'll link value
* objects to. This must be a full, webapp-relative link, starting with '/'.
*
* @return Returns the actionPage.
*/
protected final String getActionPage() {
if (logger.isDebugEnabled()) {
logger.debug("getActionPage() - start");
}
if (logger.isDebugEnabled()) {
logger
.debug("getActionPage() - end - return value = "
+ actionPage);
}
return actionPage;
}
/**
* <p>
* Get a field writer appropriate to the given field.
* </p>
*
* @param session Current HTTP session we are processing.
* @param valueObjectParam
* Field for which to return an appropriate field writer.
* @param fieldParam
* Field for which to return an appropriate field writer.
* @param subFieldParam
* Sub-field within the main field, if the field is a value
* object.
* but the value stored in a hidden field.
* @param hidden
* If <code>true</code>, overrides the field definition, and gets a writer
* for a hidden field.
* @return valid field writer for the field provided.
* @throws SystemException
* thrown if the writer cannot be retrieved for any reason.
*/
public final FieldWriter getFieldWriter(
final HttpSession session,
final ValueObject valueObjectParam,
final Field fieldParam, final Field subFieldParam,
final boolean hidden)
throws SystemException {
if (logger.isDebugEnabled()) {
logger.debug("getFieldWriter(HttpSession session = " + session
+ ", ValueObject valueObjectParam = " + valueObjectParam
+ ", Field fieldParam = " + fieldParam
+ ", Field subFieldParam = " + subFieldParam
+ ", boolean hidden = " + hidden + ") - start");
}
FieldWriter fieldWriter = null;
Field writerField;
if (subFieldParam == null) {
writerField = fieldParam;
} else {
writerField = subFieldParam;
}
String type = writerField.getType();
FieldValueConvertor fieldValueConvertor = null;
if (Field.TYPE_RADIO.equals(type)) {
throw new UnsupportedOperationException("ERROR: field type '"
+ type + "' is not yet supported.");
} else if (Field.TYPE_SELECT.equals(type)) {
throw new UnsupportedOperationException("ERROR: field type '"
+ type + "' is not yet supported.");
} else if (Field.TYPE_AMOUNT.equals(type)) {
fieldValueConvertor = new NumberFieldValueConvertor("###0.##");
} else if (Field.TYPE_NUMBER.equals(type)) {
fieldValueConvertor = new NumberFieldValueConvertor("###0");
} else if (Field.TYPE_DATE.equals(type)) {
fieldValueConvertor = new DateFieldValueConvertor("yyyy-MM-dd");
} else if (Field.TYPE_TIMESTAMP.equals(type)) {
fieldValueConvertor = new TimestampFieldValueConvertor();
} else if (Field.TYPE_STRING.equals(type)) {
fieldValueConvertor = new FieldValueConvertor();
} else {
// if the type was expressly set, then start looking at the class of
// the field
PropertyDescriptor descriptor;
try {
Class valueObjectClass;
if (subFieldParam == null) {
valueObjectClass = valueObjectParam.getClass();
} else {
if (fieldParam.getDOClass() != null) {
valueObjectClass = fieldParam.getDOClass();
} else {
descriptor = PropertyUtils.getPropertyDescriptor(
valueObjectParam, fieldParam.getPath());
valueObjectClass = descriptor.getPropertyType();
// if it is a collection, we _need_ the class
// specifically defined
if (Collection.class
.isAssignableFrom(valueObjectClass)) {
throw new NullPointerException(
"ERROR: you must specify a data object "
+ "class for collection '"
+ fieldParam.getName() + "'.");
}
}
}
descriptor = getPropertyDescriptor(valueObjectClass,
writerField.getPath());
} catch (NoSuchMethodException e) {
logger.error("getFieldWriter - error getting property for "
+ fieldParam, e);
throw new SystemException(e);
} catch (IllegalAccessException e) {
logger.error("getFieldWriter - error getting property for "
+ fieldParam, e);
throw new SystemException(e);
} catch (InvocationTargetException e) {
logger.error("getFieldWriter - error getting property for "
+ fieldParam, e);
throw new SystemException(e);
}
// if it is a value object, use a value object writer
Class propertyType = descriptor.getPropertyType();
if (ValueObject.class.isAssignableFrom(propertyType)
|| Collection.class.isAssignableFrom(
propertyType)) {
// if it is a hidden field, use the value object convertor
// to put the id out
if (hidden || writerField.isHidden()) {
fieldValueConvertor = new ValueObjectFieldValueConvertor();
} else {
// for a collection type, you _must_ specify the value
// object class to use!!!
Class dOClass;
boolean multiple = Collection.class
.isAssignableFrom(propertyType);
if (writerField.getDOClass() != null) {
dOClass = writerField.getDOClass();
} else if (multiple) {
throw new SystemException(
"ERROR: for collection field '"
+ writerField.getPath()
+ "', you must specify attribute 'class' in the "
+ "ivata masks configuration.");
} else {
dOClass = propertyType;
}
PersistenceSession persistenceSession = persistenceManager
.openSession(session);
try {
Collection allEntries = persistenceManager.findAll(
persistenceSession, dOClass);
int listHeight = 1;
if (multiple) {
listHeight =
FieldWriterConstants.MULTIPLE_LIST_HEIGHT;
}
fieldWriter = newValueObjectFieldWriter(writerField,
actionPage, allEntries, formatter, listHeight,
multiple);
} finally {
persistenceSession.close();
}
}
} else {
// default to the string representation of the field
fieldValueConvertor = new FieldValueConvertor();
}
}
// if we specified a field value convertor, then either make a
// hidden or text/textarea field (otherwise, it must have been the
// 'special case' of a value object field writer, which is dealt
// with above.
if (fieldValueConvertor != null) {
if (hidden || writerField.isHidden()) {
fieldWriter = newHiddenFieldWriter(writerField,
fieldValueConvertor, formatter);
} else if (Field.TYPE_PASSWORD.equals(type)) {
fieldWriter = newPasswordFieldWriter(writerField,
fieldValueConvertor, formatter);
} else if (Field.TYPE_TEXTAREA.equals(type)) {
fieldWriter = newTextAreaFieldWriter(writerField,
fieldValueConvertor, formatter);
} else {
fieldWriter = newTextFieldWriter(writerField,
fieldValueConvertor, formatter);
}
}
if (logger.isDebugEnabled()) {
logger.debug("getFieldWriter - end - return value = "
+ fieldWriter);
}
return fieldWriter;
}
/**
* <p>
* Find the descriptor for the class and id provided.
* </p>
*
* @param theClass
* class for which to find a descriptor.
* @param name
* name of the property for which to find a descriptor.
* @return valid property descriptor describing the property called
* <code>name</code> in class <code>theClass</code>.
*/
private PropertyDescriptor getPropertyDescriptor(final Class theClass,
final String name) {
if (logger.isDebugEnabled()) {
logger.debug("getPropertyDescriptor(Class theClass = " + theClass
+ ", String name = " + name + ") - start");
}
PropertyDescriptor[] allDescriptors = PropertyUtils
.getPropertyDescriptors(theClass);
PropertyDescriptor descriptor = null;
// this property might be nested
int dotIndex = name.indexOf('.');
String propertyName = null;
if (dotIndex == -1) {
propertyName = name;
} else {
propertyName = name.substring(0, dotIndex);
}
for (int i = 0; i < allDescriptors.length; i++) {
if (propertyName.equals(allDescriptors[i].getName())) {
descriptor = allDescriptors[i];
}
}
// if this is a nested property, get the next in the chain
if (dotIndex == -1) {
if (logger.isDebugEnabled()) {
logger.debug("getPropertyDescriptor - end - return value = "
+ descriptor);
}
return descriptor;
} else {
PropertyDescriptor returnPropertyDescriptor = getPropertyDescriptor(
theClass, name.substring(++dotIndex));
if (logger.isDebugEnabled()) {
logger.debug("getPropertyDescriptor - end - return value = "
+ returnPropertyDescriptor);
}
return returnPropertyDescriptor;
}
}
/**
* Override this method if you need a different field writer for passwords.
*
* @param field
* <copyDoc>Refer to {@link TextAreaFieldWriter#TextAreaFieldWriter}.
* </copyDoc>
* @param convertor
* <copyDoc>Refer to {@link TextAreaFieldWriter#TextAreaFieldWriter}.
* </copyDoc>
* @param formatterParam
* <copyDoc>Refer to {@link TextAreaFieldWriter#TextAreaFieldWriter}.
* </copyDoc>
* @return valid text area field writer.
*/
protected FieldWriter newPasswordFieldWriter(final Field field,
final FieldValueConvertor convertor,
final HTMLFormatter formatterParam) {
if (logger.isDebugEnabled()) {
logger.debug("newPasswordFieldWriter(Field field = " + field
+ ", FieldValueConvertor convertor = " + convertor
+ ", HTMLFormatter formatterParam = " + formatterParam
+ ") - start");
}
FieldWriter returnFieldWriter = new PasswordFieldWriter(field,
convertor, formatterParam);
if (logger.isDebugEnabled()) {
logger.debug("newPasswordFieldWriter - end - return value = "
+ returnFieldWriter);
}
return returnFieldWriter;
}
/**
* Override this method if you need a different field writer for text areas.
*
* @param field
* <copyDoc>Refer to {@link TextAreaFieldWriter#TextAreaFieldWriter}.
* </copyDoc>
* @param convertor
* <copyDoc>Refer to {@link TextAreaFieldWriter#TextAreaFieldWriter}.
* </copyDoc>
* @param formatterParam
* <copyDoc>Refer to {@link TextAreaFieldWriter#TextAreaFieldWriter}.
* </copyDoc>
* @return valid text area field writer.
*/
protected FieldWriter newTextAreaFieldWriter(final Field field,
final FieldValueConvertor convertor,
final HTMLFormatter formatterParam) {
if (logger.isDebugEnabled()) {
logger.debug("newTextAreaFieldWriter(Field field = " + field
+ ", FieldValueConvertor convertor = " + convertor
+ ", HTMLFormatter formatterParam = " + formatterParam
+ ") - start");
}
FieldWriter returnFieldWriter = new TextAreaFieldWriter(field,
convertor, formatterParam);
if (logger.isDebugEnabled()) {
logger.debug("newTextAreaFieldWriter - end - return value = "
+ returnFieldWriter);
}
return returnFieldWriter;
}
/**
* Override this method if you need a different field writer for hidden
* fields.
*
* @param fieldParam
* <copyDoc>Refer to {@link TextFieldWriter#TextFieldWriter}.</copyDoc>
* @param convertorParam
* <copyDoc>Refer to {@link TextFieldWriter#TextFieldWriter}.</copyDoc>
* @param formatterParam
* <copyDoc>Refer to {@link TextFieldWriter#TextFieldWriter}.</copyDoc>
* @return valid text field writer.
*/
protected FieldWriter newHiddenFieldWriter(final Field fieldParam,
final FieldValueConvertor convertorParam,
final HTMLFormatter formatterParam) {
if (logger.isDebugEnabled()) {
logger.debug("newHiddenFieldWriter(Field fieldParam = "
+ fieldParam + ", FieldValueConvertor convertorParam = "
+ convertorParam + ", HTMLFormatter formatterParam = "
+ formatterParam + ") - start");
}
FieldWriter returnFieldWriter = new HiddenFieldWriter(fieldParam,
convertorParam, formatterParam);
if (logger.isDebugEnabled()) {
logger.debug("newHiddenFieldWriter - end - return value = "
+ returnFieldWriter);
}
return returnFieldWriter;
}
/**
* Override this method if you need a different field writer for text
* fields.
*
* @param fieldParam
* <copyDoc>Refer to {@link TextFieldWriter#TextFieldWriter}.</copyDoc>
* @param convertorParam
* <copyDoc>Refer to {@link TextFieldWriter#TextFieldWriter}.</copyDoc>
* @param formatterParam
* <copyDoc>Refer to {@link TextFieldWriter#TextFieldWriter}.</copyDoc>
* @return valid text field writer.
*/
protected FieldWriter newTextFieldWriter(final Field fieldParam,
final FieldValueConvertor convertorParam,
final HTMLFormatter formatterParam) {
if (logger.isDebugEnabled()) {
logger.debug("newTextFieldWriter(Field fieldParam = " + fieldParam
+ ", FieldValueConvertor convertorParam = "
+ convertorParam + ", HTMLFormatter formatterParam = "
+ formatterParam + ") - start");
}
FieldWriter returnFieldWriter = new TextFieldWriter(fieldParam,
convertorParam, formatterParam);
if (logger.isDebugEnabled()) {
logger.debug("newTextFieldWriter - end - return value = "
+ returnFieldWriter);
}
return returnFieldWriter;
}
/**
* Override this method if you need a different field writer for value
* objects.
*
* @param fieldParam
* <copyDoc>Refer to {@link ValueObjectFieldWriter#ValueObjectFieldWriter}.
* </copyDoc>
* @param actionPageParam
* <copyDoc>Refer to {@link ValueObjectFieldWriter#ValueObjectFieldWriter}.
* </copyDoc>
* @param allValueObjectsParam
* <copyDoc>Refer to {@link ValueObjectFieldWriter#ValueObjectFieldWriter}.
* </copyDoc>
* @param formatterParam
* <copyDoc>Refer to {@link ValueObjectFieldWriter#ValueObjectFieldWriter}.
* </copyDoc>
* @param listHeightParam
* <copyDoc>Refer to {@link ValueObjectFieldWriter#ValueObjectFieldWriter}.
* </copyDoc>
* @param multipleParam
* <copyDoc>Refer to {@link ValueObjectFieldWriter#ValueObjectFieldWriter}.
* </copyDoc>
* @return valid field writer.
*/
protected FieldWriter newValueObjectFieldWriter(final Field fieldParam,
final String actionPageParam,
final Collection allValueObjectsParam,
final HTMLFormatter formatterParam, final int listHeightParam,
final boolean multipleParam) {
if (logger.isDebugEnabled()) {
logger.debug("newValueObjectFieldWriter(Field fieldParam = "
+ fieldParam + ", String actionPageParam = "
+ actionPageParam + ", Collection allValueObjectsParam = "
+ allValueObjectsParam
+ ", HTMLFormatter formatterParam = " + formatterParam
+ ", int listHeightParam = " + listHeightParam
+ ", boolean multipleParam = " + multipleParam
+ ") - start");
}
FieldWriter returnFieldWriter = new ValueObjectFieldWriter(fieldParam,
actionPageParam, allValueObjectsParam, formatterParam,
listHeightParam, multipleParam);
if (logger.isDebugEnabled()) {
logger.debug("newValueObjectFieldWriter - end - return value = "
+ returnFieldWriter);
}
return returnFieldWriter;
}
}
|