Added: ofbiz/trunk/framework/widget/src/org/ofbiz/widget/model/ModelFormField.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/widget/src/org/ofbiz/widget/model/ModelFormField.java?rev=1652852&view=auto ============================================================================== --- ofbiz/trunk/framework/widget/src/org/ofbiz/widget/model/ModelFormField.java (added) +++ ofbiz/trunk/framework/widget/src/org/ofbiz/widget/model/ModelFormField.java Sun Jan 18 21:03:40 2015 @@ -0,0 +1,3806 @@ +/******************************************************************************* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + *******************************************************************************/ +package org.ofbiz.widget.model; + +import java.io.IOException; +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.text.DateFormat; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; +import java.util.TimeZone; + +import org.ofbiz.base.conversion.ConversionException; +import org.ofbiz.base.conversion.DateTimeConverters; +import org.ofbiz.base.conversion.DateTimeConverters.StringToTimestamp; +import org.ofbiz.base.util.BshUtil; +import org.ofbiz.base.util.Debug; +import org.ofbiz.base.util.GeneralException; +import org.ofbiz.base.util.ObjectType; +import org.ofbiz.base.util.StringUtil; +import org.ofbiz.base.util.UtilCodec; +import org.ofbiz.base.util.UtilDateTime; +import org.ofbiz.base.util.UtilFormatOut; +import org.ofbiz.base.util.UtilGenerics; +import org.ofbiz.base.util.UtilMisc; +import org.ofbiz.base.util.UtilProperties; +import org.ofbiz.base.util.UtilValidate; +import org.ofbiz.base.util.UtilXml; +import org.ofbiz.base.util.collections.FlexibleMapAccessor; +import org.ofbiz.base.util.collections.MapStack; +import org.ofbiz.base.util.string.FlexibleStringExpander; +import org.ofbiz.entity.Delegator; +import org.ofbiz.entity.GenericEntity; +import org.ofbiz.entity.GenericEntityException; +import org.ofbiz.entity.GenericValue; +import org.ofbiz.entity.condition.EntityCondition; +import org.ofbiz.entity.finder.EntityFinderUtil; +import org.ofbiz.entity.model.ModelEntity; +import org.ofbiz.entity.util.EntityUtil; +import org.ofbiz.widget.WidgetWorker; +import org.ofbiz.widget.model.CommonWidgetModels.AutoEntityParameters; +import org.ofbiz.widget.model.CommonWidgetModels.AutoServiceParameters; +import org.ofbiz.widget.model.CommonWidgetModels.Image; +import org.ofbiz.widget.model.CommonWidgetModels.Link; +import org.ofbiz.widget.model.CommonWidgetModels.Parameter; +import org.ofbiz.widget.model.ModelForm.UpdateArea; +import org.ofbiz.widget.renderer.FormStringRenderer; +import org.w3c.dom.Element; + +import bsh.EvalError; +import bsh.Interpreter; + +/** + * Models the <field> element. + * + * @see <code>widget-form.xsd</code> + */ +public class ModelFormField { + + /* + * ----------------------------------------------------------------------- * + * DEVELOPERS PLEASE READ + * ----------------------------------------------------------------------- * + * + * This model is intended to be a read-only data structure that represents + * an XML element. Outside of object construction, the class should not + * have any behaviors. All behavior should be contained in model visitors. + * + * Instances of this class will be shared by multiple threads - therefore + * it is immutable. DO NOT CHANGE THE OBJECT'S STATE AT RUN TIME! + * + */ + + public static final String module = ModelFormField.class.getName(); + + public static ModelFormField from(ModelFormFieldBuilder builder) { + return new ModelFormField(builder); + } + + private final FlexibleStringExpander action; + private final String attributeName; + private final boolean encodeOutput; + private final String entityName; + private final FlexibleMapAccessor<Object> entryAcsr; + private final String event; + private final FieldInfo fieldInfo; + private final String fieldName; + private final String headerLink; + private final String headerLinkStyle; + private final String idName; + private final FlexibleMapAccessor<Map<String, ? extends Object>> mapAcsr; + private final ModelForm modelForm; + private final String name; + private final List<UpdateArea> onChangeUpdateAreas; + private final List<UpdateArea> onClickUpdateAreas; + private final String parameterName; + private final Integer position; + private final String redWhen; + private final Boolean requiredField; + private final String requiredFieldStyle; + private final boolean separateColumn; + private final String serviceName; + private final Boolean sortField; + private final String sortFieldAscStyle; + private final String sortFieldDescStyle; + private final String sortFieldHelpText; + private final String sortFieldStyle; + private final FlexibleStringExpander title; + private final String titleAreaStyle; + private final String titleStyle; + private final FlexibleStringExpander tooltip; + private final String tooltipStyle; + private final FlexibleStringExpander useWhen; + private final String widgetAreaStyle; + private final String widgetStyle; + + private ModelFormField(ModelFormFieldBuilder builder) { + this.action = builder.getAction(); + this.attributeName = builder.getAttributeName(); + this.encodeOutput = builder.getEncodeOutput(); + this.entityName = builder.getEntityName(); + this.entryAcsr = builder.getEntryAcsr(); + this.event = builder.getEvent(); + if (builder.getFieldInfo() != null) { + this.fieldInfo = builder.getFieldInfo().copy(this); + } else { + this.fieldInfo = null; + } + this.fieldName = builder.getFieldName(); + this.headerLink = builder.getHeaderLink(); + this.headerLinkStyle = builder.getHeaderLinkStyle(); + this.idName = builder.getIdName(); + this.mapAcsr = builder.getMapAcsr(); + this.modelForm = builder.getModelForm(); + this.name = builder.getName(); + if (builder.getOnChangeUpdateAreas().isEmpty()) { + this.onChangeUpdateAreas = Collections.emptyList(); + } else { + this.onChangeUpdateAreas = Collections.unmodifiableList(new ArrayList<UpdateArea>(builder.getOnChangeUpdateAreas())); + } + if (builder.getOnClickUpdateAreas().isEmpty()) { + this.onClickUpdateAreas = Collections.emptyList(); + } else { + this.onClickUpdateAreas = Collections.unmodifiableList(new ArrayList<UpdateArea>(builder.getOnClickUpdateAreas())); + } + this.parameterName = builder.getParameterName(); + this.position = builder.getPosition(); + this.redWhen = builder.getRedWhen(); + this.requiredField = builder.getRequiredField(); + this.requiredFieldStyle = builder.getRequiredFieldStyle(); + this.separateColumn = builder.getSeparateColumn(); + this.serviceName = builder.getServiceName(); + this.sortField = builder.getSortField(); + this.sortFieldAscStyle = builder.getSortFieldAscStyle(); + this.sortFieldDescStyle = builder.getSortFieldDescStyle(); + this.sortFieldHelpText = builder.getSortFieldHelpText(); + this.sortFieldStyle = builder.getSortFieldStyle(); + this.title = builder.getTitle(); + this.titleAreaStyle = builder.getTitleAreaStyle(); + this.titleStyle = builder.getTitleStyle(); + this.tooltip = builder.getTooltip(); + this.tooltipStyle = builder.getTooltipStyle(); + this.useWhen = builder.getUseWhen(); + this.widgetAreaStyle = builder.getWidgetAreaStyle(); + this.widgetStyle = builder.getWidgetStyle(); + } + + public FlexibleStringExpander getAction() { + return action; + } + + public String getAction(Map<String, ? extends Object> context) { + if (UtilValidate.isNotEmpty(this.action)) + return action.expandString(context); + return null; + } + + /** + * Gets the name of the Service Attribute (aka Parameter) that corresponds + * with this field. This can be used to get additional information about the field. + * Use the getServiceName() method to get the Entity name that the field is in. + * + * @return returns the name of the Service Attribute + */ + public String getAttributeName() { + if (UtilValidate.isNotEmpty(this.attributeName)) + return this.attributeName; + return this.name; + } + + public String getCurrentContainerId(Map<String, Object> context) { + ModelForm modelForm = this.getModelForm(); + String idName = FlexibleStringExpander.expandString(this.getIdName(), context); + + if (modelForm != null) { + Integer itemIndex = (Integer) context.get("itemIndex"); + if ("list".equals(modelForm.getType()) || "multi".equals(modelForm.getType())) { + if (itemIndex != null) { + return idName + modelForm.getItemIndexSeparator() + itemIndex.intValue(); + } + } + } + return idName; + } + + public boolean getEncodeOutput() { + return this.encodeOutput; + } + + public String getEntityName() { + if (UtilValidate.isNotEmpty(this.entityName)) + return this.entityName; + return this.modelForm.getDefaultEntityName(); + } + + /** + * Gets the entry from the context that corresponds to this field; if this + * form is being rendered in an error condition (ie isError in the context + * is true) then the value will be retrieved from the parameters Map in + * the context. + * + * @param context the context + * @return returns the entry from the context that corresponds to this field + */ + public String getEntry(Map<String, ? extends Object> context) { + return this.getEntry(context, ""); + } + + public String getEntry(Map<String, ? extends Object> context, String defaultValue) { + Boolean isError = (Boolean) context.get("isError"); + Boolean useRequestParameters = (Boolean) context.get("useRequestParameters"); + + Locale locale = (Locale) context.get("locale"); + if (locale == null) + locale = Locale.getDefault(); + TimeZone timeZone = (TimeZone) context.get("timeZone"); + if (timeZone == null) + timeZone = TimeZone.getDefault(); + + String returnValue; + + // if useRequestParameters is TRUE then parameters will always be used, if FALSE then parameters will never be used + // if isError is TRUE and useRequestParameters is not FALSE (ie is null or TRUE) then parameters will be used + if ((Boolean.TRUE.equals(isError) && !Boolean.FALSE.equals(useRequestParameters)) + || (Boolean.TRUE.equals(useRequestParameters))) { + //Debug.logInfo("Getting entry, isError true so getting from parameters for field " + this.getName() + " of form " + this.modelForm.getName(), module); + Map<String, Object> parameters = UtilGenerics.checkMap(context.get("parameters"), String.class, Object.class); + String parameterName = this.getParameterName(context); + if (parameters != null && parameters.get(parameterName) != null) { + Object parameterValue = parameters.get(parameterName); + if (parameterValue instanceof String) { + returnValue = (String) parameterValue; + } else { + // we might want to do something else here in the future, but for now this is probably best + Debug.logWarning("Found a non-String parameter value for field [" + this.getModelForm().getName() + "." + + this.getFieldName() + "]", module); + returnValue = defaultValue; + } + } else { + returnValue = defaultValue; + } + } else { + //Debug.logInfo("Getting entry, isError false so getting from Map in context for field " + this.getName() + " of form " + this.modelForm.getName(), module); + Map<String, ? extends Object> dataMap = this.getMap(context); + boolean dataMapIsContext = false; + if (dataMap == null) { + //Debug.logInfo("Getting entry, no Map found with name " + this.getMapName() + ", using context for field " + this.getName() + " of form " + this.modelForm.getName(), module); + dataMap = context; + dataMapIsContext = true; + } + Object retVal = null; + if (UtilValidate.isNotEmpty(this.entryAcsr)) { + if (dataMap instanceof GenericEntity) { + GenericEntity genEnt = (GenericEntity) dataMap; + if (genEnt.getModelEntity().isField(this.entryAcsr.getOriginalName())) { + retVal = genEnt.get(this.entryAcsr.getOriginalName(), locale); + } else { + //TODO: this may never come up, but if necessary use the FlexibleStringExander to eval the name first: String evaled = this.entryAcsr + } + } else { + retVal = this.entryAcsr.get(dataMap, locale); + } + } else { + // if no entry name was specified, use the field's name + if (dataMap.containsKey(this.name)) { + retVal = dataMap.get(this.name); + } + } + + // this is a special case to fill in fields during a create by default from parameters passed in + if (dataMapIsContext && retVal == null && !Boolean.FALSE.equals(useRequestParameters)) { + Map<String, ? extends Object> parameters = UtilGenerics.checkMap(context.get("parameters")); + if (parameters != null) { + if (UtilValidate.isNotEmpty(this.entryAcsr)) + retVal = this.entryAcsr.get(parameters); + else + retVal = parameters.get(this.name); + } + } + + if (retVal != null) { + // format string based on the user's locale and time zone + if (retVal instanceof Double || retVal instanceof Float || retVal instanceof BigDecimal) { + NumberFormat nf = NumberFormat.getInstance(locale); + nf.setMaximumFractionDigits(10); + return nf.format(retVal); + } else if (retVal instanceof java.sql.Date) { + DateFormat df = UtilDateTime.toDateFormat(UtilDateTime.DATE_FORMAT, timeZone, null); + return df.format((java.util.Date) retVal); + } else if (retVal instanceof java.sql.Time) { + DateFormat df = UtilDateTime.toTimeFormat(UtilDateTime.TIME_FORMAT, timeZone, null); + return df.format((java.util.Date) retVal); + } else if (retVal instanceof java.sql.Timestamp) { + DateFormat df = UtilDateTime.toDateTimeFormat(UtilDateTime.DATE_TIME_FORMAT, timeZone, null); + return df.format((java.util.Date) retVal); + } else if (retVal instanceof java.util.Date) { + DateFormat df = UtilDateTime.toDateTimeFormat("EEE MMM dd hh:mm:ss z yyyy", timeZone, null); + return df.format((java.util.Date) retVal); + } else { + returnValue = retVal.toString(); + } + } else { + returnValue = defaultValue; + } + } + + if (this.getEncodeOutput() && returnValue != null) { + UtilCodec.SimpleEncoder simpleEncoder = (UtilCodec.SimpleEncoder) context.get("simpleEncoder"); + if (simpleEncoder != null) + returnValue = simpleEncoder.encode(returnValue); + } + return returnValue; + } + + public FlexibleMapAccessor<Object> getEntryAcsr() { + return entryAcsr; + } + + public String getEntryName() { + if (UtilValidate.isNotEmpty(this.entryAcsr)) + return this.entryAcsr.getOriginalName(); + return this.name; + } + + public String getEvent() { + return event; + } + + public FieldInfo getFieldInfo() { + return fieldInfo; + } + + /** + * Gets the name of the Entity Field that corresponds + * with this field. This can be used to get additional information about the field. + * Use the getEntityName() method to get the Entity name that the field is in. + * + * @return return the name of the Entity Field that corresponds with this field + */ + public String getFieldName() { + if (UtilValidate.isNotEmpty(this.fieldName)) + return this.fieldName; + return this.name; + } + + public String getHeaderLink() { + return headerLink; + } + + public String getHeaderLinkStyle() { + return headerLinkStyle; + } + + public String getIdName() { + if (UtilValidate.isNotEmpty(idName)) + return idName; + return this.modelForm.getName() + "_" + this.getFieldName(); + } + + public Map<String, ? extends Object> getMap(Map<String, ? extends Object> context) { + if (UtilValidate.isEmpty(this.mapAcsr)) + return this.modelForm.getDefaultMap(context); //Debug.logInfo("Getting Map from default of the form because of no mapAcsr for field " + this.getName(), module); + + // Debug.logInfo("Getting Map from mapAcsr for field " + this.getName() + ", map-name=" + mapAcsr.getOriginalName() + ", context type=" + context.getClass().toString(), module); + Map<String, ? extends Object> result = null; + try { + result = mapAcsr.get(context); + } catch (java.lang.ClassCastException e) { + String errMsg = "Got an unexpected object type (not a Map) for map-name [" + mapAcsr.getOriginalName() + + "] in field with name [" + this.getName() + "]: " + e.getMessage(); + Debug.logError(errMsg, module); + throw new ClassCastException(errMsg); + } + return result; + } + + public FlexibleMapAccessor<Map<String, ? extends Object>> getMapAcsr() { + return mapAcsr; + } + + /** Get the name of the Map in the form context that contains the entry, + * available from the getEntryName() method. This entry is used to + * pre-populate the field widget when not in an error condition. In an + * error condition the parameter name is used to get the value from the + * parameters Map. + * + * @return returns the name of the Map in the form context that contains the entry + */ + public String getMapName() { + if (UtilValidate.isNotEmpty(this.mapAcsr)) + return this.mapAcsr.getOriginalName(); + return this.modelForm.getDefaultMapName(); + } + + public ModelForm getModelForm() { + return modelForm; + } + + public String getName() { + return name; + } + + public List<UpdateArea> getOnChangeUpdateAreas() { + return onChangeUpdateAreas; + } + + public List<UpdateArea> getOnClickUpdateAreas() { + return onClickUpdateAreas; + } + + public String getParameterName() { + return parameterName; + } + + /** + * Get the name to use for the parameter for this field in the form interpreter. + * For HTML forms this is the request parameter name. + * + * @return returns the name to use for the parameter for this field in the form interpreter + */ + public String getParameterName(Map<String, ? extends Object> context) { + String baseName; + if (UtilValidate.isNotEmpty(this.parameterName)) + baseName = this.parameterName; + else + baseName = this.name; + + Integer itemIndex = (Integer) context.get("itemIndex"); + if (itemIndex != null && "multi".equals(this.modelForm.getType())) { + return baseName + this.modelForm.getItemIndexSeparator() + itemIndex.intValue(); + } else { + return baseName; + } + } + + public int getPosition() { + if (this.position == null) + return 1; + return position.intValue(); + } + + public String getRedWhen() { + return redWhen; + } + + public boolean getRequiredField() { + return this.requiredField != null ? this.requiredField : false; + } + + public String getRequiredFieldStyle() { + if (UtilValidate.isNotEmpty(this.requiredFieldStyle)) + return this.requiredFieldStyle; + return this.modelForm.getDefaultRequiredFieldStyle(); + } + + public boolean getSeparateColumn() { + return this.separateColumn; + } + + public String getServiceName() { + if (UtilValidate.isNotEmpty(this.serviceName)) + return this.serviceName; + return this.modelForm.getDefaultServiceName(); + } + + public Boolean getSortField() { + return sortField; + } + + public String getSortFieldAscStyle() { + return sortFieldAscStyle; + } + + public String getSortFieldDescStyle() { + return sortFieldDescStyle; + } + + public String getSortFieldHelpText() { + return sortFieldHelpText; + } + + public String getSortFieldHelpText(Map<String, Object> context) { + return FlexibleStringExpander.expandString(this.sortFieldHelpText, context); + } + + public String getSortFieldStyle() { + if (UtilValidate.isNotEmpty(this.sortFieldStyle)) + return this.sortFieldStyle; + return this.modelForm.getDefaultSortFieldStyle(); + } + + public String getSortFieldStyleAsc() { + if (UtilValidate.isNotEmpty(this.sortFieldAscStyle)) + return this.sortFieldAscStyle; + return this.modelForm.getDefaultSortFieldAscStyle(); + } + + public String getSortFieldStyleDesc() { + if (UtilValidate.isNotEmpty(this.sortFieldDescStyle)) + return this.sortFieldDescStyle; + return this.modelForm.getDefaultSortFieldDescStyle(); + } + + public FlexibleStringExpander getTitle() { + return title; + } + + public String getTitle(Map<String, Object> context) { + if (UtilValidate.isNotEmpty(this.title)) + return title.expandString(context); + + // create a title from the name of this field; expecting a Java method/field style name, ie productName or productCategoryId + if (UtilValidate.isEmpty(this.name)) + return ""; // this should never happen, ie name is required + + // search for a localized label for the field's name + Map<String, String> uiLabelMap = UtilGenerics.checkMap(context.get("uiLabelMap")); + if (uiLabelMap != null) { + String titleFieldName = "FormFieldTitle_" + this.name; + String localizedName = uiLabelMap.get(titleFieldName); + if (!localizedName.equals(titleFieldName)) { + return localizedName; + } + } else { + Debug.logWarning("Could not find uiLabelMap in context while rendering form " + this.modelForm.getName(), module); + } + + // create a title from the name of this field; expecting a Java method/field style name, ie productName or productCategoryId + StringBuilder autoTitlewriter = new StringBuilder(); + + // always use upper case first letter... + autoTitlewriter.append(Character.toUpperCase(this.name.charAt(0))); + + // just put spaces before the upper case letters + for (int i = 1; i < this.name.length(); i++) { + char curChar = this.name.charAt(i); + if (Character.isUpperCase(curChar)) { + autoTitlewriter.append(' '); + } + autoTitlewriter.append(curChar); + } + + return autoTitlewriter.toString(); + } + + public String getTitleAreaStyle() { + if (UtilValidate.isNotEmpty(this.titleAreaStyle)) + return this.titleAreaStyle; + return this.modelForm.getDefaultTitleAreaStyle(); + } + + public String getTitleStyle() { + if (UtilValidate.isNotEmpty(this.titleStyle)) + return this.titleStyle; + return this.modelForm.getDefaultTitleStyle(); + } + + public FlexibleStringExpander getTooltip() { + return tooltip; + } + + public String getTooltip(Map<String, Object> context) { + String tooltipString = ""; + if (UtilValidate.isNotEmpty(tooltip)) + tooltipString = tooltip.expandString(context); + if (this.getEncodeOutput()) { + UtilCodec.SimpleEncoder simpleEncoder = (UtilCodec.SimpleEncoder) context.get("simpleEncoder"); + if (simpleEncoder != null) + tooltipString = simpleEncoder.encode(tooltipString); + } + return tooltipString; + } + + public String getTooltipStyle() { + if (UtilValidate.isNotEmpty(this.tooltipStyle)) + return this.tooltipStyle; + return this.modelForm.getDefaultTooltipStyle(); + } + + public FlexibleStringExpander getUseWhen() { + return useWhen; + } + + public String getUseWhen(Map<String, Object> context) { + if (UtilValidate.isNotEmpty(this.useWhen)) + return this.useWhen.expandString(context); + return ""; + } + + public String getWidgetAreaStyle() { + if (UtilValidate.isNotEmpty(this.widgetAreaStyle)) + return this.widgetAreaStyle; + return this.modelForm.getDefaultWidgetAreaStyle(); + } + + public String getWidgetStyle() { + if (UtilValidate.isNotEmpty(this.widgetStyle)) + return this.widgetStyle; + return this.modelForm.getDefaultWidgetStyle(); + } + + /** + * Checks if field is a row submit field. + */ + public boolean isRowSubmit() { + if (!"multi".equals(getModelForm().getType())) + return false; + if (getFieldInfo().getFieldType() != FieldInfo.CHECK) + return false; + if (!CheckField.ROW_SUBMIT_FIELD_NAME.equals(getName())) + return false; + return true; + } + + public boolean isSortField() { + return this.sortField != null && this.sortField.booleanValue(); + } + + public boolean isUseWhenEmpty() { + if (this.useWhen == null) { + return true; + } + + return this.useWhen.isEmpty(); + } + + public void renderFieldString(Appendable writer, Map<String, Object> context, FormStringRenderer formStringRenderer) + throws IOException { + this.fieldInfo.renderFieldString(writer, context, formStringRenderer); + } + + /** + * the widget/interaction part will be red if the date value is + * before-now (for ex. thruDate), after-now (for ex. fromDate), or by-name (if the + * field's name or entry-name or fromDate or thruDate the corresponding + * action will be done); only applicable when the field is a timestamp + * + * @param context the context + * @return true if the field should be read otherwise false + */ + public boolean shouldBeRed(Map<String, Object> context) { + // red-when (never | before-now | after-now | by-name) "by-name" + + String redCondition = this.redWhen; + + if ("never".equals(redCondition)) + return false; + + // for performance resaons we check this first, most fields will be eliminated here and the valueOfs will not be necessary + if (UtilValidate.isEmpty(redCondition) || "by-name".equals(redCondition)) { + if ("fromDate".equals(this.name) || (this.entryAcsr != null && "fromDate".equals(this.entryAcsr.getOriginalName()))) { + redCondition = "after-now"; + } else if ("thruDate".equals(this.name) + || (this.entryAcsr != null && "thruDate".equals(this.entryAcsr.getOriginalName()))) { + redCondition = "before-now"; + } else { + return false; + } + } + + boolean isBeforeNow = false; + if ("before-now".equals(redCondition)) { + isBeforeNow = true; + } else if ("after-now".equals(redCondition)) { + isBeforeNow = false; + } else { + return false; + } + + java.sql.Date dateVal = null; + java.sql.Time timeVal = null; + java.sql.Timestamp timestampVal = null; + + //now before going on, check to see if the current entry is a valid date and/or time and get the value + String value = this.getEntry(context, null); + try { + timestampVal = java.sql.Timestamp.valueOf(value); + } catch (Exception e) { + // okay, not a timestamp... + } + + if (timestampVal == null) { + try { + dateVal = java.sql.Date.valueOf(value); + } catch (Exception e) { + // okay, not a date... + } + } + + if (timestampVal == null && dateVal == null) { + try { + timeVal = java.sql.Time.valueOf(value); + } catch (Exception e) { + // okay, not a time... + } + } + + if (timestampVal == null && dateVal == null && timeVal == null) { + return false; + } + + long nowMillis = System.currentTimeMillis(); + if (timestampVal != null) { + java.sql.Timestamp nowStamp = new java.sql.Timestamp(nowMillis); + if (!timestampVal.equals(nowStamp)) { + if (isBeforeNow) { + if (timestampVal.before(nowStamp)) { + return true; + } + } else { + if (timestampVal.after(nowStamp)) { + return true; + } + } + } + } else if (dateVal != null) { + java.sql.Date nowDate = new java.sql.Date(nowMillis); + if (!dateVal.equals(nowDate)) { + if (isBeforeNow) { + if (dateVal.before(nowDate)) { + return true; + } + } else { + if (dateVal.after(nowDate)) { + return true; + } + } + } + } else if (timeVal != null) { + java.sql.Time nowTime = new java.sql.Time(nowMillis); + if (!timeVal.equals(nowTime)) { + if (isBeforeNow) { + if (timeVal.before(nowTime)) { + return true; + } + } else { + if (timeVal.after(nowTime)) { + return true; + } + } + } + } + + return false; + } + + public boolean shouldUse(Map<String, Object> context) { + String useWhenStr = this.getUseWhen(context); + if (UtilValidate.isEmpty(useWhenStr)) + return true; + + try { + Interpreter bsh = this.modelForm.getBshInterpreter(context); + Object retVal = bsh.eval(StringUtil.convertOperatorSubstitutions(useWhenStr)); + boolean condTrue = false; + // retVal should be a Boolean, if not something weird is up... + if (retVal instanceof Boolean) { + Boolean boolVal = (Boolean) retVal; + condTrue = boolVal.booleanValue(); + } else { + throw new IllegalArgumentException("Return value from use-when condition eval was not a Boolean: " + + (retVal != null ? retVal.getClass().getName() : "null") + " [" + retVal + "] on the field " + this.name + + " of form " + this.modelForm.getName()); + } + + return condTrue; + } catch (EvalError e) { + String errMsg = "Error evaluating BeanShell use-when condition [" + useWhenStr + "] on the field " + this.name + + " of form " + this.modelForm.getName() + ": " + e.toString(); + Debug.logError(e, errMsg, module); + //Debug.logError("For use-when eval error context is: " + context, module); + throw new IllegalArgumentException(errMsg); + } + } + + /** + * Models the <auto-complete> element. + * + * @see <code>widget-form.xsd</code> + */ + public static class AutoComplete { + private final String autoSelect; + private final String choices; + private final String frequency; + private final String fullSearch; + private final String ignoreCase; + private final String minChars; + private final String partialChars; + private final String partialSearch; + + public AutoComplete(Element element) { + this.autoSelect = element.getAttribute("auto-select"); + this.frequency = element.getAttribute("frequency"); + this.minChars = element.getAttribute("min-chars"); + this.choices = element.getAttribute("choices"); + this.partialSearch = element.getAttribute("partial-search"); + this.partialChars = element.getAttribute("partial-chars"); + this.ignoreCase = element.getAttribute("ignore-case"); + this.fullSearch = element.getAttribute("full-search"); + } + + public String getAutoSelect() { + return this.autoSelect; + } + + public String getChoices() { + return this.choices; + } + + public String getFrequency() { + return this.frequency; + } + + public String getFullSearch() { + return this.fullSearch; + } + + public String getIgnoreCase() { + return this.ignoreCase; + } + + public String getMinChars() { + return this.minChars; + } + + public String getPartialChars() { + return this.partialChars; + } + + public String getPartialSearch() { + return this.partialSearch; + } + } + + /** + * Models the <check> element. + * + * @see <code>widget-form.xsd</code> + */ + public static class CheckField extends FieldInfoWithOptions { + public final static String ROW_SUBMIT_FIELD_NAME = "_rowSubmit"; + private final FlexibleStringExpander allChecked; + + private CheckField(CheckField original, ModelFormField modelFormField) { + super(original, modelFormField); + this.allChecked = original.allChecked; + } + + public CheckField(Element element, ModelFormField modelFormField) { + super(element, modelFormField); + allChecked = FlexibleStringExpander.getInstance(element.getAttribute("all-checked")); + } + + public CheckField(int fieldSource, ModelFormField modelFormField) { + super(fieldSource, FieldInfo.CHECK, modelFormField); + this.allChecked = FlexibleStringExpander.getInstance(""); + } + + public CheckField(ModelFormField modelFormField) { + super(FieldInfo.SOURCE_EXPLICIT, FieldInfo.CHECK, modelFormField); + this.allChecked = FlexibleStringExpander.getInstance(""); + } + + @Override + public void accept(ModelFieldVisitor visitor) throws Exception { + visitor.visit(this); + } + + @Override + public FieldInfo copy(ModelFormField modelFormField) { + return new CheckField(this, modelFormField); + } + + public FlexibleStringExpander getAllChecked() { + return allChecked; + } + + public Boolean isAllChecked(Map<String, Object> context) { + String allCheckedStr = this.allChecked.expandString(context); + if (!allCheckedStr.isEmpty()) + return Boolean.valueOf("true".equals(allCheckedStr)); + else + return null; + } + + @Override + public void renderFieldString(Appendable writer, Map<String, Object> context, FormStringRenderer formStringRenderer) + throws IOException { + formStringRenderer.renderCheckField(writer, context, this); + } + } + + /** + * Models the <container> element. + * + * @see <code>widget-form.xsd</code> + */ + public static class ContainerField extends FieldInfo { + + private ContainerField(ContainerField original, ModelFormField modelFormField) { + super(original.getFieldSource(), original.getFieldType(), modelFormField); + } + + public ContainerField(Element element, ModelFormField modelFormField) { + super(element, modelFormField); + } + + public ContainerField(int fieldSource, int fieldType, ModelFormField modelFormField) { + super(fieldSource, fieldType, modelFormField); + } + + @Override + public void accept(ModelFieldVisitor visitor) throws Exception { + visitor.visit(this); + } + + @Override + public FieldInfo copy(ModelFormField modelFormField) { + return new ContainerField(this, modelFormField); + } + + @Override + public void renderFieldString(Appendable writer, Map<String, Object> context, FormStringRenderer formStringRenderer) + throws IOException { + formStringRenderer.renderContainerFindField(writer, context, this); + } + } + + /** + * Models the <date-find> element. + * + * @see <code>widget-form.xsd</code> + */ + public static class DateFindField extends DateTimeField { + private final String defaultOptionFrom; + private final String defaultOptionThru; + + private DateFindField(DateFindField original, ModelFormField modelFormField) { + super(original, modelFormField); + this.defaultOptionFrom = original.defaultOptionFrom; + this.defaultOptionThru = original.defaultOptionThru; + } + + public DateFindField(Element element, ModelFormField modelFormField) { + super(element, modelFormField); + this.defaultOptionFrom = element.getAttribute("default-option-from"); + this.defaultOptionThru = element.getAttribute("default-option-thru"); + } + + public DateFindField(int fieldSource, ModelFormField modelFormField) { + super(fieldSource, modelFormField); + this.defaultOptionFrom = "greaterThanEqualTo"; + this.defaultOptionThru = "lessThanEqualTo"; + } + + public DateFindField(int fieldSource, String type) { + super(fieldSource, type); + this.defaultOptionFrom = "greaterThanEqualTo"; + this.defaultOptionThru = "lessThanEqualTo"; + } + + @Override + public void accept(ModelFieldVisitor visitor) throws Exception { + visitor.visit(this); + } + + @Override + public FieldInfo copy(ModelFormField modelFormField) { + return new DateFindField(this, modelFormField); + } + + public String getDefaultOptionFrom() { + return this.defaultOptionFrom; + } + + public String getDefaultOptionThru() { + return this.defaultOptionThru; + } + + @Override + public void renderFieldString(Appendable writer, Map<String, Object> context, FormStringRenderer formStringRenderer) + throws IOException { + formStringRenderer.renderDateFindField(writer, context, this); + } + } + + /** + * Models the <date-time> element. + * + * @see <code>widget-form.xsd</code> + */ + public static class DateTimeField extends FieldInfo { + private final String clock; + private final FlexibleStringExpander defaultValue; + private final String inputMethod; + private final String mask; + private final String step; + private final String type; + + protected DateTimeField(DateTimeField original, ModelFormField modelFormField) { + super(original.getFieldSource(), original.getFieldType(), modelFormField); + this.defaultValue = original.defaultValue; + this.type = original.type; + this.inputMethod = original.inputMethod; + this.clock = original.clock; + this.mask = original.mask; + this.step = original.step; + } + + public DateTimeField(Element element, ModelFormField modelFormField) { + super(element, modelFormField); + this.defaultValue = FlexibleStringExpander.getInstance(element.getAttribute("default-value")); + this.type = element.getAttribute("type"); + this.inputMethod = element.getAttribute("input-method"); + this.clock = element.getAttribute("clock"); + this.mask = element.getAttribute("mask"); + String step = element.getAttribute("step"); + if (step.isEmpty()) { + step = "1"; + } + this.step = step; + } + + public DateTimeField(int fieldSource, ModelFormField modelFormField) { + super(fieldSource, FieldInfo.DATE_TIME, modelFormField); + this.defaultValue = FlexibleStringExpander.getInstance(""); + this.type = ""; + this.inputMethod = ""; + this.clock = ""; + this.mask = ""; + this.step = "1"; + } + + public DateTimeField(int fieldSource, String type) { + super(fieldSource, FieldInfo.DATE_TIME, null); + this.defaultValue = FlexibleStringExpander.getInstance(""); + this.type = type; + this.inputMethod = ""; + this.clock = ""; + this.mask = ""; + this.step = "1"; + } + + public DateTimeField(ModelFormField modelFormField) { + super(FieldInfo.SOURCE_EXPLICIT, FieldInfo.DATE_TIME, modelFormField); + this.defaultValue = FlexibleStringExpander.getInstance(""); + this.type = ""; + this.inputMethod = ""; + this.clock = ""; + this.mask = ""; + this.step = "1"; + } + + @Override + public void accept(ModelFieldVisitor visitor) throws Exception { + visitor.visit(this); + } + + @Override + public FieldInfo copy(ModelFormField modelFormField) { + return new DateTimeField(this, modelFormField); + } + + public String getClock() { + return this.clock; + } + + /** + * Returns the default-value if specified, otherwise the current date, time or timestamp + * + * @param context Context Map + * @return Default value string for date-time + */ + public String getDefaultDateTimeString(Map<String, Object> context) { + if (UtilValidate.isNotEmpty(this.defaultValue)) + return this.getDefaultValue(context); + + if ("date".equals(this.type)) + return (new java.sql.Date(System.currentTimeMillis())).toString(); + else if ("time".equals(this.type)) + return (new java.sql.Time(System.currentTimeMillis())).toString(); + else + return UtilDateTime.nowTimestamp().toString(); + } + + public FlexibleStringExpander getDefaultValue() { + return defaultValue; + } + + public String getDefaultValue(Map<String, Object> context) { + if (this.defaultValue != null) { + return this.defaultValue.expandString(context); + } else { + return ""; + } + } + + public String getInputMethod() { + return this.inputMethod; + } + + public String getMask() { + return this.mask; + } + + public String getStep() { + return this.step; + } + + public String getType() { + return type; + } + + @Override + public void renderFieldString(Appendable writer, Map<String, Object> context, FormStringRenderer formStringRenderer) + throws IOException { + formStringRenderer.renderDateTimeField(writer, context, this); + } + } + + /** + * Models the <display-entity> element. + * + * @see <code>widget-form.xsd</code> + */ + public static class DisplayEntityField extends DisplayField { + private final boolean cache; + private final String entityName; + private final String keyFieldName; + private final SubHyperlink subHyperlink; + + private DisplayEntityField(DisplayEntityField original, ModelFormField modelFormField) { + super(original, modelFormField); + this.cache = original.cache; + this.entityName = original.entityName; + this.keyFieldName = original.keyFieldName; + if (original.subHyperlink != null) { + this.subHyperlink = new SubHyperlink(original.subHyperlink, modelFormField); + } else { + this.subHyperlink = null; + } + } + + public DisplayEntityField(Element element, ModelFormField modelFormField) { + super(element, modelFormField); + this.cache = !"false".equals(element.getAttribute("cache")); + this.entityName = element.getAttribute("entity-name"); + this.keyFieldName = element.getAttribute("key-field-name"); + Element subHyperlinkElement = UtilXml.firstChildElement(element, "sub-hyperlink"); + if (subHyperlinkElement != null) { + this.subHyperlink = new SubHyperlink(subHyperlinkElement, modelFormField); + } else { + this.subHyperlink = null; + } + } + + public DisplayEntityField(int fieldSource, ModelFormField modelFormField) { + super(fieldSource, FieldInfo.DISPLAY_ENTITY, modelFormField); + this.cache = true; + this.entityName = ""; + this.keyFieldName = ""; + this.subHyperlink = null; + } + + public DisplayEntityField(ModelFormField modelFormField) { + super(FieldInfo.SOURCE_EXPLICIT, FieldInfo.DISPLAY_ENTITY, modelFormField); + this.cache = true; + this.entityName = ""; + this.keyFieldName = ""; + this.subHyperlink = null; + } + + @Override + public void accept(ModelFieldVisitor visitor) throws Exception { + visitor.visit(this); + } + + @Override + public FieldInfo copy(ModelFormField modelFormField) { + return new DisplayEntityField(this, modelFormField); + } + + public boolean getCache() { + return cache; + } + + @Override + public String getDescription(Map<String, Object> context) { + Locale locale = UtilMisc.ensureLocale(context.get("locale")); + + // rather than using the context to expand the string, lookup the given entity and use it to expand the string + GenericValue value = null; + String fieldKey = this.keyFieldName; + if (UtilValidate.isEmpty(fieldKey)) + fieldKey = getModelFormField().fieldName; + + Delegator delegator = WidgetWorker.getDelegator(context); + String fieldValue = getModelFormField().getEntry(context); + try { + value = delegator.findOne(this.entityName, this.cache, fieldKey, fieldValue); + } catch (GenericEntityException e) { + String errMsg = "Error getting value from the database for display of field [" + getModelFormField().getName() + + "] on form [" + getModelFormField().modelForm.getName() + "]: " + e.toString(); + Debug.logError(e, errMsg, module); + throw new IllegalArgumentException(errMsg); + } + + String retVal = null; + if (value != null) { + // expanding ${} stuff, passing locale explicitly to expand value string because it won't be found in the Entity + MapStack<String> localContext = MapStack.create(context); + // Rendering code might try to modify the GenericEntity instance, + // so we make a copy of it. + Map<String, Object> genericEntityClone = UtilGenerics.cast(value.clone()); + localContext.push(genericEntityClone); + + // expand with the new localContext, which is locale aware + retVal = this.getDescription().expandString(localContext, locale); + } + // try to get the entry for the field if description doesn't expand to anything + if (UtilValidate.isEmpty(retVal)) + retVal = fieldValue; + if (UtilValidate.isEmpty(retVal)) + retVal = ""; + return retVal; + } + + public String getEntityName() { + return entityName; + } + + public String getKeyFieldName() { + return keyFieldName; + } + + public SubHyperlink getSubHyperlink() { + return this.subHyperlink; + } + } + + /** + * Models the <display> element. + * + * @see <code>widget-form.xsd</code> + */ + public static class DisplayField extends FieldInfo { + private final boolean alsoHidden; + private final FlexibleStringExpander currency; + private final FlexibleStringExpander date; + private final FlexibleStringExpander defaultValue; + private final FlexibleStringExpander description; + private final FlexibleStringExpander imageLocation; + private final InPlaceEditor inPlaceEditor; + private final String size; // maximum number of characters to display + private final String type; // matches type of field, currently text or currency + + protected DisplayField(DisplayField original, ModelFormField modelFormField) { + super(original.getFieldSource(), original.getFieldType(), modelFormField); + this.alsoHidden = original.alsoHidden; + this.currency = original.currency; + this.date = original.date; + this.defaultValue = original.defaultValue; + this.description = original.description; + this.imageLocation = original.imageLocation; + this.inPlaceEditor = original.inPlaceEditor; + this.size = original.size; + this.type = original.type; + } + + public DisplayField(Element element, ModelFormField modelFormField) { + super(element, modelFormField); + this.alsoHidden = !"false".equals(element.getAttribute("also-hidden")); + this.currency = FlexibleStringExpander.getInstance(element.getAttribute("currency")); + this.date = FlexibleStringExpander.getInstance(element.getAttribute("date")); + this.defaultValue = FlexibleStringExpander.getInstance(element.getAttribute("default-value")); + this.description = FlexibleStringExpander.getInstance(element.getAttribute("description")); + this.imageLocation = FlexibleStringExpander.getInstance(element.getAttribute("image-location")); + Element inPlaceEditorElement = UtilXml.firstChildElement(element, "in-place-editor"); + if (inPlaceEditorElement != null) { + this.inPlaceEditor = new InPlaceEditor(inPlaceEditorElement); + } else { + this.inPlaceEditor = null; + } + this.size = element.getAttribute("size"); + this.type = element.getAttribute("type"); + } + + public DisplayField(int fieldSource, int fieldType, ModelFormField modelFormField) { + super(fieldSource, fieldType, modelFormField); + this.alsoHidden = true; + this.currency = FlexibleStringExpander.getInstance(""); + this.date = FlexibleStringExpander.getInstance(""); + this.defaultValue = FlexibleStringExpander.getInstance(""); + this.description = FlexibleStringExpander.getInstance(""); + this.imageLocation = FlexibleStringExpander.getInstance(""); + this.inPlaceEditor = null; + this.size = ""; + this.type = ""; + } + + public DisplayField(int fieldSource, ModelFormField modelFormField) { + super(fieldSource, FieldInfo.DISPLAY, modelFormField); + this.alsoHidden = true; + this.currency = FlexibleStringExpander.getInstance(""); + this.date = FlexibleStringExpander.getInstance(""); + this.defaultValue = FlexibleStringExpander.getInstance(""); + this.description = FlexibleStringExpander.getInstance(""); + this.imageLocation = FlexibleStringExpander.getInstance(""); + this.inPlaceEditor = null; + this.size = ""; + this.type = ""; + } + + public DisplayField(ModelFormField modelFormField) { + super(FieldInfo.SOURCE_EXPLICIT, FieldInfo.DISPLAY, modelFormField); + this.alsoHidden = true; + this.currency = FlexibleStringExpander.getInstance(""); + this.date = FlexibleStringExpander.getInstance(""); + this.defaultValue = FlexibleStringExpander.getInstance(""); + this.description = FlexibleStringExpander.getInstance(""); + this.imageLocation = FlexibleStringExpander.getInstance(""); + this.inPlaceEditor = null; + this.size = ""; + this.type = ""; + } + + @Override + public void accept(ModelFieldVisitor visitor) throws Exception { + visitor.visit(this); + } + + @Override + public FieldInfo copy(ModelFormField modelFormField) { + return new DisplayField(this, modelFormField); + } + + public boolean getAlsoHidden() { + return alsoHidden; + } + + public FlexibleStringExpander getCurrency() { + return currency; + } + + public FlexibleStringExpander getDate() { + return date; + } + + public FlexibleStringExpander getDefaultValue() { + return defaultValue; + } + + public String getDefaultValue(Map<String, Object> context) { + if (this.defaultValue != null) { + return this.defaultValue.expandString(context); + } else { + return ""; + } + } + + public FlexibleStringExpander getDescription() { + return description; + } + + public String getDescription(Map<String, Object> context) { + String retVal = null; + if (UtilValidate.isNotEmpty(this.description)) + retVal = this.description.expandString(context); + else + retVal = getModelFormField().getEntry(context); + + if (UtilValidate.isEmpty(retVal)) { + retVal = this.getDefaultValue(context); + } else if ("currency".equals(type)) { + retVal = retVal.replaceAll(" ", " "); // FIXME : encoding currency is a problem for some locale, we should not have any in retVal other case may arise in future... + Locale locale = (Locale) context.get("locale"); + if (locale == null) + locale = Locale.getDefault(); + String isoCode = null; + if (UtilValidate.isNotEmpty(this.currency)) + isoCode = this.currency.expandString(context); + + try { + BigDecimal parsedRetVal = (BigDecimal) ObjectType.simpleTypeConvert(retVal, "BigDecimal", null, null, locale, + true); + retVal = UtilFormatOut.formatCurrency(parsedRetVal, isoCode, locale, 10); // we set the max to 10 digits as an hack to not round numbers in the ui + } catch (GeneralException e) { + String errMsg = "Error formatting currency value [" + retVal + "]: " + e.toString(); + Debug.logError(e, errMsg, module); + throw new IllegalArgumentException(errMsg); + } + } else if ("date".equals(this.type) && retVal.length() > 10) { + Locale locale = (Locale) context.get("locale"); + if (locale == null) { + locale = Locale.getDefault(); + } + + StringToTimestamp stringToTimestamp = new DateTimeConverters.StringToTimestamp(); + Timestamp timestamp = null; + try { + timestamp = stringToTimestamp.convert(retVal); + Date date = new Date(timestamp.getTime()); + + DateFormat dateFormatter = DateFormat.getDateInstance(DateFormat.SHORT, locale); + retVal = dateFormatter.format(date); + } catch (ConversionException e) { + String errMsg = "Error formatting date using default instead [" + retVal + "]: " + e.toString(); + Debug.logError(e, errMsg, module); + // create default date value from timestamp string + retVal = retVal.substring(0, 10); + } + + } else if ("date-time".equals(this.type) && retVal.length() > 16) { + Locale locale = (Locale) context.get("locale"); + TimeZone timeZone = (TimeZone) context.get("timeZone"); + if (locale == null) { + locale = Locale.getDefault(); + } + if (timeZone == null) { + timeZone = TimeZone.getDefault(); + } + + StringToTimestamp stringToTimestamp = new DateTimeConverters.StringToTimestamp(); + Timestamp timestamp = null; + try { + timestamp = stringToTimestamp.convert(retVal); + Date date = new Date(timestamp.getTime()); + + DateFormat dateFormatter = UtilDateTime.toDateTimeFormat(null, timeZone, locale); + retVal = dateFormatter.format(date); + } catch (ConversionException e) { + String errMsg = "Error formatting date/time using default instead [" + retVal + "]: " + e.toString(); + Debug.logError(e, errMsg, module); + // create default date/time value from timestamp string + retVal = retVal.substring(0, 16); + } + } else if ("accounting-number".equals(this.type)) { + Locale locale = (Locale) context.get("locale"); + if (locale == null) { + locale = Locale.getDefault(); + } + try { + Double parsedRetVal = (Double) ObjectType.simpleTypeConvert(retVal, "Double", null, locale, false); + String template = UtilProperties.getPropertyValue("arithmetic", "accounting-number.format", + "#,##0.00;(#,##0.00)"); + retVal = UtilFormatOut.formatDecimalNumber(parsedRetVal.doubleValue(), template, locale); + } catch (GeneralException e) { + String errMsg = "Error formatting number [" + retVal + "]: " + e.toString(); + Debug.logError(e, errMsg, module); + throw new IllegalArgumentException(errMsg); + } + } + if (UtilValidate.isNotEmpty(this.description) && retVal != null && this.getModelFormField().getEncodeOutput()) { + UtilCodec.SimpleEncoder simpleEncoder = (UtilCodec.SimpleEncoder) context.get("simpleEncoder"); + if (simpleEncoder != null) { + retVal = simpleEncoder.encode(retVal); + } + } + return retVal; + } + + public FlexibleStringExpander getImageLocation() { + return imageLocation; + } + + public String getImageLocation(Map<String, Object> context) { + if (this.imageLocation != null) + return this.imageLocation.expandString(context); + return ""; + } + + public InPlaceEditor getInPlaceEditor() { + return this.inPlaceEditor; + } + + public String getSize() { + return this.size; + } + + public String getType() { + return this.type; + } + + @Override + public void renderFieldString(Appendable writer, Map<String, Object> context, FormStringRenderer formStringRenderer) + throws IOException { + formStringRenderer.renderDisplayField(writer, context, this); + } + } + + /** + * Models the <drop-down> element. + * + * @see <code>widget-form.xsd</code> + */ + public static class DropDownField extends FieldInfoWithOptions { + private final boolean allowEmpty; + private final boolean allowMulti; + private final AutoComplete autoComplete; + private final String current; + private final FlexibleStringExpander currentDescription; + private final int otherFieldSize; + private final String size; + private final SubHyperlink subHyperlink; + private final String textSize; + + private DropDownField(DropDownField original, ModelFormField modelFormField) { + super(original, modelFormField); + this.allowEmpty = original.allowEmpty; + this.allowMulti = original.allowMulti; + this.autoComplete = original.autoComplete; + this.current = original.current; + this.currentDescription = original.currentDescription; + this.otherFieldSize = original.otherFieldSize; + this.size = original.size; + if (original.subHyperlink != null) { + this.subHyperlink = new SubHyperlink(original.subHyperlink, modelFormField); + } else { + this.subHyperlink = null; + } + this.textSize = original.textSize; + } + + public DropDownField(Element element, ModelFormField modelFormField) { + super(element, modelFormField); + this.allowEmpty = "true".equals(element.getAttribute("allow-empty")); + this.allowMulti = "true".equals(element.getAttribute("allow-multiple")); + Element autoCompleteElement = UtilXml.firstChildElement(element, "auto-complete"); + if (autoCompleteElement != null) { + this.autoComplete = new AutoComplete(autoCompleteElement); + } else { + this.autoComplete = null; + } + this.current = element.getAttribute("current"); + this.currentDescription = FlexibleStringExpander.getInstance(element.getAttribute("current-description")); + int otherFieldSize = 0; + String sizeStr = element.getAttribute("other-field-size"); + if (!sizeStr.isEmpty()) { + try { + otherFieldSize = Integer.parseInt(sizeStr); + } catch (Exception e) { + Debug.logError("Could not parse the size value of the text element: [" + sizeStr + + "], setting to the default of 0", module); + } + } + this.otherFieldSize = otherFieldSize; + String size = element.getAttribute("size"); + if (size.isEmpty()) { + size = "1"; + } + this.size = size; + Element subHyperlinkElement = UtilXml.firstChildElement(element, "sub-hyperlink"); + if (subHyperlinkElement != null) { + this.subHyperlink = new SubHyperlink(subHyperlinkElement, this.getModelFormField()); + } else { + this.subHyperlink = null; + } + String textSize = element.getAttribute("text-size"); + if (textSize.isEmpty()) { + textSize = "0"; + } + this.textSize = textSize; + } + + public DropDownField(int fieldSource, List<OptionSource> optionSources) { + super(fieldSource, FieldInfo.DROP_DOWN, optionSources); + this.allowEmpty = false; + this.allowMulti = false; + this.autoComplete = null; + this.current = ""; + this.currentDescription = FlexibleStringExpander.getInstance(""); + this.otherFieldSize = 0; + this.size = "1"; + this.subHyperlink = null; + this.textSize = "0"; + } + + public DropDownField(int fieldSource, ModelFormField modelFormField) { + super(fieldSource, FieldInfo.DROP_DOWN, modelFormField); + this.allowEmpty = false; + this.allowMulti = false; + this.autoComplete = null; + this.current = ""; + this.currentDescription = FlexibleStringExpander.getInstance(""); + this.otherFieldSize = 0; + this.size = "1"; + this.subHyperlink = null; + this.textSize = "0"; + } + + public DropDownField(ModelFormField modelFormField) { + super(FieldInfo.SOURCE_EXPLICIT, FieldInfo.DROP_DOWN, modelFormField); + this.allowEmpty = false; + this.allowMulti = false; + this.autoComplete = null; + this.current = ""; + this.currentDescription = FlexibleStringExpander.getInstance(""); + this.otherFieldSize = 0; + this.size = "1"; + this.subHyperlink = null; + this.textSize = "0"; + } + + @Override + public void accept(ModelFieldVisitor visitor) throws Exception { + visitor.visit(this); + } + + @Override + public FieldInfo copy(ModelFormField modelFormField) { + return new DropDownField(this, modelFormField); + } + + public boolean getAllowMulti() { + return allowMulti; + } + + public AutoComplete getAutoComplete() { + return this.autoComplete; + } + + public String getCurrent() { + if (UtilValidate.isEmpty(this.current)) + return "first-in-list"; + return this.current; + } + + public FlexibleStringExpander getCurrentDescription() { + return currentDescription; + } + + public String getCurrentDescription(Map<String, Object> context) { + if (this.currentDescription == null) + return null; + return this.currentDescription.expandString(context); + } + + public int getOtherFieldSize() { + return this.otherFieldSize; + } + + /** + * Get the name to use for the parameter for this field in the form interpreter. + * For HTML forms this is the request parameter name. + * @param context the context + * @return returns the name to use for the parameter for this field in the form interpreter. + */ + public String getParameterNameOther(Map<String, Object> context) { + String baseName; + if (UtilValidate.isNotEmpty(getModelFormField().parameterName)) + baseName = getModelFormField().parameterName; + else + baseName = getModelFormField().name; + + baseName += "_OTHER"; + Integer itemIndex = (Integer) context.get("itemIndex"); + if (itemIndex != null && "multi".equals(getModelFormField().modelForm.getType())) { + return baseName + getModelFormField().modelForm.getItemIndexSeparator() + itemIndex.intValue(); + } else { + return baseName; + } + } + + public String getSize() { + return this.size; + } + + public SubHyperlink getSubHyperlink() { + return this.subHyperlink; + } + + public String getTextSize() { + return this.textSize; + } + + public boolean getAllowEmpty() { + return this.allowEmpty; + } + + public boolean getAllowMultiple() { + return this.allowMulti; + } + + @Override + public void renderFieldString(Appendable writer, Map<String, Object> context, FormStringRenderer formStringRenderer) + throws IOException { + formStringRenderer.renderDropDownField(writer, context, this); + } + } + + /** + * Models the <entity-options> element. + * + * @see <code>widget-form.xsd</code> + */ + public static class EntityOptions extends OptionSource { + private final boolean cache; + private final List<EntityFinderUtil.ConditionExpr> constraintList; + private final FlexibleStringExpander description; + private final String entityName; + private final String filterByDate; + private final String keyFieldName; + private final List<String> orderByList; + + public EntityOptions(Element entityOptionsElement, ModelFormField modelFormField) { + super(modelFormField); + this.cache = !"false".equals(entityOptionsElement.getAttribute("cache")); + List<? extends Element> constraintElements = UtilXml.childElementList(entityOptionsElement, "entity-constraint"); + if (!constraintElements.isEmpty()) { + List<EntityFinderUtil.ConditionExpr> constraintList = new ArrayList<EntityFinderUtil.ConditionExpr>( + constraintElements.size()); + for (Element constraintElement : constraintElements) { + constraintList.add(new EntityFinderUtil.ConditionExpr(constraintElement)); + } + this.constraintList = Collections.unmodifiableList(constraintList); + } else { + this.constraintList = Collections.emptyList(); + } + this.description = FlexibleStringExpander.getInstance(entityOptionsElement.getAttribute("description")); + this.entityName = entityOptionsElement.getAttribute("entity-name"); + this.filterByDate = entityOptionsElement.getAttribute("filter-by-date"); + this.keyFieldName = entityOptionsElement.getAttribute("key-field-name"); + List<? extends Element> orderByElements = UtilXml.childElementList(entityOptionsElement, "entity-order-by"); + if (!orderByElements.isEmpty()) { + List<String> orderByList = new ArrayList<String>(orderByElements.size()); + for (Element orderByElement : orderByElements) { + orderByList.add(orderByElement.getAttribute("field-name")); + } + this.orderByList = Collections.unmodifiableList(orderByList); + } else { + this.orderByList = Collections.emptyList(); + } + } + + private EntityOptions(EntityOptions original, ModelFormField modelFormField) { + super(modelFormField); + this.cache = original.cache; + this.constraintList = original.constraintList; + this.description = original.description; + this.entityName = original.entityName; + this.filterByDate = original.filterByDate; + this.keyFieldName = original.keyFieldName; + this.orderByList = original.orderByList; + } + + public EntityOptions(ModelFormField modelFormField) { + super(modelFormField); + this.cache = true; + this.constraintList = Collections.emptyList(); + this.description = FlexibleStringExpander.getInstance(""); + this.entityName = ""; + this.filterByDate = ""; + this.keyFieldName = ""; + this.orderByList = Collections.emptyList(); + } + + @Override + public void addOptionValues(List<OptionValue> optionValues, Map<String, Object> context, Delegator delegator) { + // first expand any conditions that need expanding based on the current context + EntityCondition findCondition = null; + if (UtilValidate.isNotEmpty(this.constraintList)) { + List<EntityCondition> expandedConditionList = new LinkedList<EntityCondition>(); + for (EntityFinderUtil.Condition condition : constraintList) { + ModelEntity modelEntity = delegator.getModelEntity(this.entityName); + if (modelEntity == null) { + throw new IllegalArgumentException("Error in entity-options: could not find entity [" + this.entityName + + "]"); + } + EntityCondition createdCondition = condition.createCondition(context, modelEntity, + delegator.getModelFieldTypeReader(modelEntity)); + if (createdCondition != null) { + expandedConditionList.add(createdCondition); + } + } + findCondition = EntityCondition.makeCondition(expandedConditionList); + } + + try { + Locale locale = UtilMisc.ensureLocale(context.get("locale")); + + List<GenericValue> values = null; + values = delegator.findList(this.entityName, findCondition, null, this.orderByList, null, this.cache); + + // filter-by-date if requested + if ("true".equals(this.filterByDate)) { + values = EntityUtil.filterByDate(values, true); + } else if (!"false".equals(this.filterByDate)) { + // not explicitly true or false, check to see if has fromDate and thruDate, if so do the filter + ModelEntity modelEntity = delegator.getModelEntity(this.entityName); + if (modelEntity != null && modelEntity.isField("fromDate") && modelEntity.isField("thruDate")) { + values = EntityUtil.filterByDate(values, true); + } + } + + for (GenericValue value : values) { + // add key and description with string expansion, ie expanding ${} stuff, passing locale explicitly to expand value string because it won't be found in the Entity + MapStack<String> localContext = MapStack.create(context); + // Rendering code might try to modify the GenericEntity instance, + // so we make a copy of it. + Map<String, Object> genericEntityClone = UtilGenerics.cast(value.clone()); + localContext.push(genericEntityClone); + + // expand with the new localContext, which is locale aware + String optionDesc = this.description.expandString(localContext, locale); + + Object keyFieldObject = value.get(this.getKeyFieldName()); + if (keyFieldObject == null) { + throw new IllegalArgumentException( + "The entity-options identifier (from key-name attribute, or default to the field name) [" + + this.getKeyFieldName() + "], may not be a valid key field name for the entity [" + + this.entityName + "]."); + } + String keyFieldValue = keyFieldObject.toString(); + optionValues.add(new OptionValue(keyFieldValue, optionDesc)); + } + } catch (GenericEntityException e) { + Debug.logError(e, "Error getting entity options in form", module); + } + } + + @Override + public OptionSource copy(ModelFormField modelFormField) { + return new EntityOptions(this, modelFormField); + } + + public boolean getCache() { + return cache; + } + + public List<EntityFinderUtil.ConditionExpr> getConstraintList() { + return constraintList; + } + + public FlexibleStringExpander getDescription() { + return description; + } + + public String getEntityName() { + return entityName; + } + + public String getFilterByDate() { + return filterByDate; + } + + public String getKeyFieldName() { + if (UtilValidate.isNotEmpty(this.keyFieldName)) + return this.keyFieldName; + return getModelFormField().getFieldName(); // get the modelFormField fieldName + } + + public List<String> getOrderByList() { + return orderByList; + } + } + + public static abstract class FieldInfoWithOptions extends FieldInfo { + + public static String getDescriptionForOptionKey(String key, List<OptionValue> allOptionValues) { + if (UtilValidate.isEmpty(key)) + return ""; + + if (UtilValidate.isEmpty(allOptionValues)) + return key; + + for (OptionValue optionValue : allOptionValues) { + if (key.equals(optionValue.getKey())) { + return optionValue.getDescription(); + } + } + + // if we get here we didn't find a match, just return the key + return key; + } + + private final FlexibleStringExpander noCurrentSelectedKey; + private final List<OptionSource> optionSources; + + public FieldInfoWithOptions(Element element, ModelFormField modelFormField) { + super(element, modelFormField); + this.noCurrentSelectedKey = FlexibleStringExpander.getInstance(element.getAttribute("no-current-selected-key")); + // read all option and entity-options sub-elements, maintaining order + ArrayList<OptionSource> optionSources = new ArrayList<OptionSource>(); + List<? extends Element> childElements = UtilXml.childElementList(element); + if (childElements.size() > 0) { + for (Element childElement : childElements) { + if ("option".equals(childElement.getTagName())) { + optionSources.add(new SingleOption(childElement, modelFormField)); + } else if ("list-options".equals(childElement.getTagName())) { + optionSources.add(new ListOptions(childElement, modelFormField)); + } else if ("entity-options".equals(childElement.getTagName())) { + optionSources.add(new EntityOptions(childElement, modelFormField)); + } + } + } else { + // this must be added or the multi-form select box options would not show up + optionSources.add(new SingleOption("Y", " ", modelFormField)); + } + optionSources.trimToSize(); + this.optionSources = Collections.unmodifiableList(optionSources); + } + + // Copy constructor. + protected FieldInfoWithOptions(FieldInfoWithOptions original, ModelFormField modelFormField) { + super(original.getFieldSource(), original.getFieldType(), modelFormField); + this.noCurrentSelectedKey = original.noCurrentSelectedKey; + if (original.optionSources.isEmpty()) { + this.optionSources = original.optionSources; + } else { + List<OptionSource> optionSources = new ArrayList<OptionSource>(original.optionSources.size()); + for (OptionSource source : original.optionSources) { + optionSources.add(source.copy(modelFormField)); + } + this.optionSources = Collections.unmodifiableList(optionSources); + } + } + + protected FieldInfoWithOptions(int fieldSource, int fieldType, List<OptionSource> optionSources) { + super(fieldSource, fieldType, null); + this.noCurrentSelectedKey = FlexibleStringExpander.getInstance(""); + this.optionSources = Collections.unmodifiableList(new ArrayList<OptionSource>(optionSources)); + } + + public FieldInfoWithOptions(int fieldSource, int fieldType, ModelFormField modelFormField) { + super(fieldSource, fieldType, modelFormField); + this.noCurrentSelectedKey = FlexibleStringExpander.getInstance(""); + this.optionSources = Collections.emptyList(); + } + + public List<OptionValue> getAllOptionValues(Map<String, Object> context, Delegator delegator) { + List<OptionValue> optionValues = new LinkedList<OptionValue>(); + for (OptionSource optionSource : this.optionSources) { + optionSource.addOptionValues(optionValues, context, delegator); + } + return optionValues; + } + + public FlexibleStringExpander getNoCurrentSelectedKey() { + return noCurrentSelectedKey; + } + + public String getNoCurrentSelectedKey(Map<String, Object> context) { + return this.noCurrentSelectedKey.expandString(context); + } + + public List<OptionSource> getOptionSources() { + return optionSources; + } + } + + /** + * Models the <file> element. + * + * @see <code>widget-form.xsd</code> + */ + public static class FileField extends TextField { + + public FileField(Element element, ModelFormField modelFormField) { + super(element, modelFormField); + } + + private FileField(FileField original, ModelFormField modelFormField) { + super(original, modelFormField); + } + + public FileField(int fieldSource, ModelFormField modelFormField) { + super(fieldSource, modelFormField); + } + + @Override + public void accept(ModelFieldVisitor visitor) throws Exception { + visitor.visit(this); + } + + @Override + public FieldInfo copy(ModelFormField modelFormField) { + return new FileField(this, modelFormField); + } + + @Override + public void renderFieldString(Appendable writer, Map<String, Object> context, FormStringRenderer formStringRenderer) + throws IOException { + formStringRenderer.renderFileField(writer, context, this); + } + } + + /** + * Models the <hidden> element. + * + * @see <code>widget-form.xsd</code> + */ + public static class HiddenField extends FieldInfo { + private final FlexibleStringExpander value; + + public HiddenField(Element element, ModelFormField modelFormField) { + super(element, modelFormField); + this.value = FlexibleStringExpander.getInstance(element.getAttribute("value")); + } + + private HiddenField(HiddenField original, ModelFormField modelFormField) { + super(original.getFieldSource(), original.getFieldType(), modelFormField); + this.value = original.value; + } + + public HiddenField(int fieldSource, ModelFormField modelFormField) { + super(fieldSource, FieldInfo.HIDDEN, modelFormField); + this.value = FlexibleStringExpander.getInstance(""); + } + + public HiddenField(ModelFormField modelFormField) { + super(FieldInfo.SOURCE_EXPLICIT, FieldInfo.HIDDEN, modelFormField); + this.value = FlexibleStringExpander.getInstance(""); + } + + @Override + public void accept(ModelFieldVisitor visitor) throws Exception { + visitor.visit(this); + } + + @Override + public FieldInfo copy(ModelFormField modelFormField) { + return new HiddenField(this, modelFormField); + } + + public FlexibleStringExpander getValue() { + return value; + } + + public String getValue(Map<String, Object> context) { + if (UtilValidate.isNotEmpty(this.value)) { + String valueEnc = this.value.expandString(context); + UtilCodec.SimpleEncoder simpleEncoder = (UtilCodec.SimpleEncoder) context.get("simpleEncoder"); + if (simpleEncoder != null) { + valueEnc = simpleEncoder.encode(valueEnc); + } + return valueEnc; + } else { + return getModelFormField().getEntry(context); + } + } + + @Override + public void renderFieldString(Appendable writer, Map<String, Object> context, FormStringRenderer formStringRenderer) + throws IOException { + formStringRenderer.renderHiddenField(writer, context, this); + } + } + + /** + * Models the <hyperlink> element. + * + * @see <code>widget-form.xsd</code> + */ + public static class HyperlinkField extends FieldInfo { + + private final boolean alsoHidden; + private final FlexibleStringExpander confirmationMsgExdr; + private final FlexibleStringExpander description; + private final boolean requestConfirmation; + private final Link link; + public HyperlinkField(Element element, ModelFormField modelFormField) { + super(element, modelFormField); + this.alsoHidden = !"false".equals(element.getAttribute("also-hidden")); + this.confirmationMsgExdr = FlexibleStringExpander.getInstance(element.getAttribute("confirmation-message")); [... 1699 lines stripped ...] |
Free forum by Nabble | Edit this page |