Added: ofbiz/trunk/framework/widget/src/org/ofbiz/widget/renderer/FormRenderer.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/widget/src/org/ofbiz/widget/renderer/FormRenderer.java?rev=1652852&view=auto ============================================================================== --- ofbiz/trunk/framework/widget/src/org/ofbiz/widget/renderer/FormRenderer.java (added) +++ ofbiz/trunk/framework/widget/src/org/ofbiz/widget/renderer/FormRenderer.java Sun Jan 18 21:03:40 2015 @@ -0,0 +1,1231 @@ +/******************************************************************************* + * 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.renderer; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; + +import org.ofbiz.base.util.Debug; +import org.ofbiz.base.util.UtilGenerics; +import org.ofbiz.base.util.UtilMisc; +import org.ofbiz.base.util.UtilValidate; +import org.ofbiz.base.util.collections.MapStack; +import org.ofbiz.base.util.string.FlexibleStringExpander; +import org.ofbiz.entity.GenericEntity; +import org.ofbiz.entity.GenericEntityException; +import org.ofbiz.entity.util.EntityListIterator; +import org.ofbiz.widget.WidgetWorker; +import org.ofbiz.widget.model.AbstractModelAction; +import org.ofbiz.widget.model.FieldInfo; +import org.ofbiz.widget.model.ModelForm; +import org.ofbiz.widget.model.ModelForm.FieldGroup; +import org.ofbiz.widget.model.ModelForm.FieldGroupBase; +import org.ofbiz.widget.model.ModelFormField; + +/** + * A form rendering engine. + * + */ +public class FormRenderer { + + /* + * ----------------------------------------------------------------------- * + * DEVELOPERS PLEASE READ + * ----------------------------------------------------------------------- * + * + * An instance of this class is created by each thread for each form that + * is rendered. If you need to keep track of things while rendering, then + * this is the place to do it. In other words, feel free to modify this + * object's state (except for the final fields of course). + * + */ + + public static final String module = FormRenderer.class.getName(); + + public static String getCurrentContainerId(ModelForm modelForm, Map<String, Object> context) { + Locale locale = UtilMisc.ensureLocale(context.get("locale")); + String retVal = FlexibleStringExpander.expandString(modelForm.getContainerId(), context, locale); + Integer itemIndex = (Integer) context.get("itemIndex"); + if (itemIndex != null && "list".equals(modelForm.getType())) { + return retVal + modelForm.getItemIndexSeparator() + itemIndex.intValue(); + } + return retVal; + } + + public static String getCurrentFormName(ModelForm modelForm, Map<String, Object> context) { + Integer itemIndex = (Integer) context.get("itemIndex"); + String formName = (String) context.get("formName"); + if (UtilValidate.isEmpty(formName)) { + formName = modelForm.getName(); + } + if (itemIndex != null && "list".equals(modelForm.getType())) { + return formName + modelForm.getItemIndexSeparator() + itemIndex.intValue(); + } else { + return formName; + } + } + + public static String getFocusFieldName(ModelForm modelForm, Map<String, Object> context) { + String focusFieldName = (String) context.get(modelForm.getName().concat(".focusFieldName")); + if (focusFieldName == null) { + return ""; + } + return focusFieldName; + } + + private final ModelForm modelForm; + private final FormStringRenderer formStringRenderer; + private String focusFieldName; + + public FormRenderer(ModelForm modelForm, FormStringRenderer formStringRenderer) { + this.modelForm = modelForm; + this.formStringRenderer = formStringRenderer; + this.focusFieldName = modelForm.getFocusFieldName(); + } + + private Collection<List<ModelFormField>> getFieldListsByPosition(List<ModelFormField> modelFormFieldList) { + Map<Integer, List<ModelFormField>> fieldsByPosition = new TreeMap<Integer, List<ModelFormField>>(); + for (ModelFormField modelFormField : modelFormFieldList) { + Integer position = Integer.valueOf(modelFormField.getPosition()); + List<ModelFormField> fieldListByPosition = fieldsByPosition.get(position); + if (fieldListByPosition == null) { + fieldListByPosition = new LinkedList<ModelFormField>(); + fieldsByPosition.put(position, fieldListByPosition); + } + fieldListByPosition.add(modelFormField); + } + return fieldsByPosition.values(); + } + + public String getFocusFieldName() { + return focusFieldName; + } + + private List<ModelFormField> getHiddenIgnoredFields(Map<String, Object> context, Set<String> alreadyRendered, + List<ModelFormField> fieldList, int position) { + /* + * Method does not reference internal state - should be moved to another class. + */ + List<ModelFormField> hiddenIgnoredFieldList = new LinkedList<ModelFormField>(); + for (ModelFormField modelFormField : fieldList) { + // with position == -1 then gets all the hidden fields + if (position != -1 && modelFormField.getPosition() != position) { + continue; + } + FieldInfo fieldInfo = modelFormField.getFieldInfo(); + + // render hidden/ignored field widget + switch (fieldInfo.getFieldType()) { + case FieldInfo.HIDDEN: + case FieldInfo.IGNORED: + if (modelFormField.shouldUse(context)) { + hiddenIgnoredFieldList.add(modelFormField); + if (alreadyRendered != null) + alreadyRendered.add(modelFormField.getName()); + } + break; + + case FieldInfo.DISPLAY: + case FieldInfo.DISPLAY_ENTITY: + ModelFormField.DisplayField displayField = (ModelFormField.DisplayField) fieldInfo; + if (displayField.getAlsoHidden() && modelFormField.shouldUse(context)) { + hiddenIgnoredFieldList.add(modelFormField); + // don't add to already rendered here, or the display won't ger rendered: if (alreadyRendered != null) alreadyRendered.add(modelFormField.getName()); + } + break; + + case FieldInfo.HYPERLINK: + ModelFormField.HyperlinkField hyperlinkField = (ModelFormField.HyperlinkField) fieldInfo; + if (hyperlinkField.getAlsoHidden() && modelFormField.shouldUse(context)) { + hiddenIgnoredFieldList.add(modelFormField); + // don't add to already rendered here, or the hyperlink won't ger rendered: if (alreadyRendered != null) alreadyRendered.add(modelFormField.getName()); + } + break; + } + } + return hiddenIgnoredFieldList; + } + + private List<FieldGroupBase> getInbetweenList(FieldGroup startFieldGroup, FieldGroup endFieldGroup) { + ArrayList<FieldGroupBase> inbetweenList = new ArrayList<FieldGroupBase>(); + boolean firstFound = false; + String startFieldGroupId = null; + String endFieldGroupId = null; + if (endFieldGroup != null) { + endFieldGroupId = endFieldGroup.getId(); + } + if (startFieldGroup == null) { + firstFound = true; + } else { + startFieldGroupId = startFieldGroup.getId(); + } + Iterator<FieldGroupBase> iter = modelForm.getFieldGroupList().iterator(); + while (iter.hasNext()) { + FieldGroupBase obj = iter.next(); + if (obj instanceof ModelForm.Banner) { + if (firstFound) + inbetweenList.add(obj); + } else { + FieldGroup fieldGroup = (FieldGroup) obj; + String fieldGroupId = fieldGroup.getId(); + if (!firstFound) { + if (fieldGroupId.equals(startFieldGroupId)) { + firstFound = true; + continue; + } + } + if (firstFound) { + if (fieldGroupId.equals(endFieldGroupId)) { + break; + } else { + inbetweenList.add(fieldGroup); + } + } + } + } + return inbetweenList; + } + + /** + * Renders this form to a String, i.e. in a text format, as defined with the + * FormStringRenderer implementation. + * + * @param writer The Writer that the form text will be written to + * @param context Map containing the form context; the following are + * reserved words in this context: parameters (Map), isError (Boolean), + * itemIndex (Integer, for lists only, otherwise null), bshInterpreter, + * formName (String, optional alternate name for form, defaults to the + * value of the name attribute) + */ + public void render(Appendable writer, Map<String, Object> context) + throws Exception { + // increment the paginator, only for list and multi forms + if ("list".equals(modelForm.getType()) || "multi".equals(modelForm.getType())) { + WidgetWorker.incrementPaginatorNumber(context); + } + + // Populate the viewSize and viewIndex so they are available for use during form actions + context.put("viewIndex", Paginator.getViewIndex(modelForm, context)); + context.put("viewSize", Paginator.getViewSize(modelForm, context)); + + modelForm.runFormActions(context); + + // if this is a list form, don't useRequestParameters + if ("list".equals(modelForm.getType()) || "multi".equals(modelForm.getType())) { + context.put("useRequestParameters", Boolean.FALSE); + } + + // find the highest position number to get the max positions used + int positions = 1; + for (ModelFormField modelFormField : modelForm.getFieldList()) { + int curPos = modelFormField.getPosition(); + if (curPos > positions) { + positions = curPos; + } + FieldInfo currentFieldInfo = modelFormField.getFieldInfo(); + if (currentFieldInfo == null) { + throw new IllegalArgumentException( + "Error rendering form, a field has no FieldInfo, ie no sub-element for the type of field for field named: " + + modelFormField.getName()); + } + } + + if ("single".equals(modelForm.getType())) { + this.renderSingleFormString(writer, context, positions); + } else if ("list".equals(modelForm.getType())) { + this.renderListFormString(writer, context, positions); + } else if ("multi".equals(modelForm.getType())) { + this.renderMultiFormString(writer, context, positions); + } else if ("upload".equals(modelForm.getType())) { + this.renderSingleFormString(writer, context, positions); + } else { + if (UtilValidate.isEmpty(modelForm.getType())) { + throw new IllegalArgumentException("The form 'type' tag is missing or empty on the form with the name " + + modelForm.getName()); + } else { + throw new IllegalArgumentException("The form type " + modelForm.getType() + + " is not supported for form with name " + modelForm.getName()); + } + } + } + + private int renderHeaderRow(Appendable writer, Map<String, Object> context) + throws IOException { + int maxNumOfColumns = 0; + + // We will render one title/column for all the fields with the same name + // in this model: we can have more fields with the same name when use-when + // conditions are used or when a form is extended or when the fields are + // automatically retrieved by a service or entity definition. + List<ModelFormField> tempFieldList = new LinkedList<ModelFormField>(); + tempFieldList.addAll(modelForm.getFieldList()); + for (int j = 0; j < tempFieldList.size(); j++) { + ModelFormField modelFormField = tempFieldList.get(j); + for (int i = j + 1; i < tempFieldList.size(); i++) { + ModelFormField curField = tempFieldList.get(i); + if (curField.getName() != null && curField.getName().equals(modelFormField.getName())) { + tempFieldList.remove(i--); + } + } + } + + // =========================== + // Preprocessing + // =========================== + // We get a sorted (by position, ascending) set of lists; + // each list contains all the fields with that position. + Collection<List<ModelFormField>> fieldListsByPosition = this.getFieldListsByPosition(tempFieldList); + List<Map<String, List<ModelFormField>>> fieldRowsByPosition = new LinkedList<Map<String, List<ModelFormField>>>(); // this list will contain maps, each one containing the list of fields for a position + for (List<ModelFormField> mainFieldList : fieldListsByPosition) { + int numOfColumns = 0; + + List<ModelFormField> innerDisplayHyperlinkFieldsBegin = new LinkedList<ModelFormField>(); + List<ModelFormField> innerFormFields = new LinkedList<ModelFormField>(); + List<ModelFormField> innerDisplayHyperlinkFieldsEnd = new LinkedList<ModelFormField>(); + + // render title for each field, except hidden & ignored, etc + + // start by rendering all display and hyperlink fields, until we + //get to a field that should go into the form cell, then render + //the form cell with all non-display and non-hyperlink fields, then + //do a start after the first form input field and + //render all display and hyperlink fields after the form + + // prepare the two lists of display and hyperlink fields + // the fields in the first list will be rendered as columns before the + // combined column for the input fields; the fields in the second list + // will be rendered as columns after it + boolean inputFieldFound = false; + for (ModelFormField modelFormField : mainFieldList) { + FieldInfo fieldInfo = modelFormField.getFieldInfo(); + + // if the field's title is explicitly set to "" (title="") then + // the header is not created for it; this is useful for position list + // where one line can be rendered with more than one row, and we + // only want to display the title header for the main row + String modelFormFieldTitle = modelFormField.getTitle(context); + if ("".equals(modelFormFieldTitle)) { + continue; + } + // don't do any header for hidden or ignored fields + if (fieldInfo.getFieldType() == FieldInfo.HIDDEN + || fieldInfo.getFieldType() == FieldInfo.IGNORED) { + continue; + } + + if (fieldInfo.getFieldType() != FieldInfo.DISPLAY + && fieldInfo.getFieldType() != FieldInfo.DISPLAY_ENTITY + && fieldInfo.getFieldType() != FieldInfo.HYPERLINK) { + inputFieldFound = true; + continue; + } + + // separate into two lists the display/hyperlink fields found before and after the first input fields + if (!inputFieldFound) { + innerDisplayHyperlinkFieldsBegin.add(modelFormField); + } else { + innerDisplayHyperlinkFieldsEnd.add(modelFormField); + } + numOfColumns++; + } + + // prepare the combined title for the column that will contain the form/input fields + for (ModelFormField modelFormField : mainFieldList) { + FieldInfo fieldInfo = modelFormField.getFieldInfo(); + + // don't do any header for hidden or ignored fields + if (fieldInfo.getFieldType() == FieldInfo.HIDDEN + || fieldInfo.getFieldType() == FieldInfo.IGNORED) { + continue; + } + + // skip all of the display/hyperlink fields + if (fieldInfo.getFieldType() == FieldInfo.DISPLAY + || fieldInfo.getFieldType() == FieldInfo.DISPLAY_ENTITY + || fieldInfo.getFieldType() == FieldInfo.HYPERLINK) { + continue; + } + + innerFormFields.add(modelFormField); + } + if (innerFormFields.size() > 0) { + numOfColumns++; + } + + if (maxNumOfColumns < numOfColumns) { + maxNumOfColumns = numOfColumns; + } + + Map<String, List<ModelFormField>> fieldRow = UtilMisc.toMap("displayBefore", innerDisplayHyperlinkFieldsBegin, + "inputFields", innerFormFields, "displayAfter", innerDisplayHyperlinkFieldsEnd, "mainFieldList", + mainFieldList); + fieldRowsByPosition.add(fieldRow); + } + // =========================== + // Rendering + // =========================== + for (Map<String, List<ModelFormField>> listsMap : fieldRowsByPosition) { + List<ModelFormField> innerDisplayHyperlinkFieldsBegin = listsMap.get("displayBefore"); + List<ModelFormField> innerFormFields = listsMap.get("inputFields"); + List<ModelFormField> innerDisplayHyperlinkFieldsEnd = listsMap.get("displayAfter"); + List<ModelFormField> mainFieldList = listsMap.get("mainFieldList"); + + int numOfCells = innerDisplayHyperlinkFieldsBegin.size() + innerDisplayHyperlinkFieldsEnd.size() + + (innerFormFields.size() > 0 ? 1 : 0); + int numOfColumnsToSpan = maxNumOfColumns - numOfCells + 1; + if (numOfColumnsToSpan < 1) { + numOfColumnsToSpan = 1; + } + + if (numOfCells > 0) { + formStringRenderer.renderFormatHeaderRowOpen(writer, context, modelForm); + + if (modelForm.getGroupColumns()) { + Iterator<ModelFormField> innerDisplayHyperlinkFieldsBeginIt = innerDisplayHyperlinkFieldsBegin.iterator(); + while (innerDisplayHyperlinkFieldsBeginIt.hasNext()) { + ModelFormField modelFormField = innerDisplayHyperlinkFieldsBeginIt.next(); + // span columns only if this is the last column in the row (not just in this first list) + if (innerDisplayHyperlinkFieldsBeginIt.hasNext() || numOfCells > innerDisplayHyperlinkFieldsBegin.size()) { + formStringRenderer.renderFormatHeaderRowCellOpen(writer, context, modelForm, modelFormField, 1); + } else { + formStringRenderer.renderFormatHeaderRowCellOpen(writer, context, modelForm, modelFormField, + numOfColumnsToSpan); + } + formStringRenderer.renderFieldTitle(writer, context, modelFormField); + formStringRenderer.renderFormatHeaderRowCellClose(writer, context, modelForm, modelFormField); + } + if (innerFormFields.size() > 0) { + // TODO: manage colspan + formStringRenderer.renderFormatHeaderRowFormCellOpen(writer, context, modelForm); + Iterator<ModelFormField> innerFormFieldsIt = innerFormFields.iterator(); + while (innerFormFieldsIt.hasNext()) { + ModelFormField modelFormField = innerFormFieldsIt.next(); + + if (modelForm.getSeparateColumns() || modelFormField.getSeparateColumn()) { + formStringRenderer.renderFormatItemRowCellOpen(writer, context, modelForm, modelFormField, 1); + } + + // render title (unless this is a submit or a reset field) + formStringRenderer.renderFieldTitle(writer, context, modelFormField); + + if (modelForm.getSeparateColumns() || modelFormField.getSeparateColumn()) { + formStringRenderer.renderFormatItemRowCellClose(writer, context, modelForm, modelFormField); + } + + if (innerFormFieldsIt.hasNext()) { + // TODO: determine somehow if this is the last one... how? + if (!modelForm.getSeparateColumns() && !modelFormField.getSeparateColumn()) { + formStringRenderer.renderFormatHeaderRowFormCellTitleSeparator(writer, context, modelForm, + modelFormField, false); + } + } + } + formStringRenderer.renderFormatHeaderRowFormCellClose(writer, context, modelForm); + } + Iterator<ModelFormField> innerDisplayHyperlinkFieldsEndIt = innerDisplayHyperlinkFieldsEnd.iterator(); + while (innerDisplayHyperlinkFieldsEndIt.hasNext()) { + ModelFormField modelFormField = innerDisplayHyperlinkFieldsEndIt.next(); + // span columns only if this is the last column in the row (not just in this first list) + if (innerDisplayHyperlinkFieldsEndIt.hasNext() || numOfCells > innerDisplayHyperlinkFieldsEnd.size()) { + formStringRenderer.renderFormatHeaderRowCellOpen(writer, context, modelForm, modelFormField, 1); + } else { + formStringRenderer.renderFormatHeaderRowCellOpen(writer, context, modelForm, modelFormField, + numOfColumnsToSpan); + } + formStringRenderer.renderFieldTitle(writer, context, modelFormField); + formStringRenderer.renderFormatHeaderRowCellClose(writer, context, modelForm, modelFormField); + } + } else { + Iterator<ModelFormField> mainFieldListIter = mainFieldList.iterator(); + while (mainFieldListIter.hasNext()) { + ModelFormField modelFormField = mainFieldListIter.next(); + + // don't do any header for hidden or ignored fields + FieldInfo fieldInfo = modelFormField.getFieldInfo(); + if (fieldInfo.getFieldType() == FieldInfo.HIDDEN + || fieldInfo.getFieldType() == FieldInfo.IGNORED) { + continue; + } + + // span columns only if this is the last column in the row (not just in this first list) + if (mainFieldListIter.hasNext() || numOfCells > mainFieldList.size()) { + formStringRenderer.renderFormatHeaderRowCellOpen(writer, context, modelForm, modelFormField, 1); + } else { + formStringRenderer.renderFormatHeaderRowCellOpen(writer, context, modelForm, modelFormField, + numOfColumnsToSpan); + } + formStringRenderer.renderFieldTitle(writer, context, modelFormField); + formStringRenderer.renderFormatHeaderRowCellClose(writer, context, modelForm, modelFormField); + } + } + + formStringRenderer.renderFormatHeaderRowClose(writer, context, modelForm); + } + } + + return maxNumOfColumns; + } + + private void renderHiddenIgnoredFields(Appendable writer, Map<String, Object> context, FormStringRenderer formStringRenderer, + List<ModelFormField> fieldList) throws IOException { + for (ModelFormField modelFormField : fieldList) { + FieldInfo fieldInfo = modelFormField.getFieldInfo(); + + // render hidden/ignored field widget + switch (fieldInfo.getFieldType()) { + case FieldInfo.HIDDEN: + case FieldInfo.IGNORED: + modelFormField.renderFieldString(writer, context, formStringRenderer); + break; + + case FieldInfo.DISPLAY: + case FieldInfo.DISPLAY_ENTITY: + case FieldInfo.HYPERLINK: + formStringRenderer.renderHiddenField(writer, context, modelFormField, modelFormField.getEntry(context)); + break; + } + } + } + + // The fields in the three lists, usually created in the preprocessing phase + // of the renderItemRows method are rendered: this will create a visual representation + // of one row (corresponding to one position). + private void renderItemRow(Appendable writer, Map<String, Object> localContext, FormStringRenderer formStringRenderer, + boolean formPerItem, List<ModelFormField> hiddenIgnoredFieldList, + List<ModelFormField> innerDisplayHyperlinkFieldsBegin, List<ModelFormField> innerFormFields, + List<ModelFormField> innerDisplayHyperlinkFieldsEnd, List<ModelFormField> mainFieldList, int position, + int numOfColumns) throws IOException { + int numOfCells = innerDisplayHyperlinkFieldsBegin.size() + innerDisplayHyperlinkFieldsEnd.size() + + (innerFormFields.size() > 0 ? 1 : 0); + int numOfColumnsToSpan = numOfColumns - numOfCells + 1; + if (numOfColumnsToSpan < 1) { + numOfColumnsToSpan = 1; + } + + // render row formatting open + formStringRenderer.renderFormatItemRowOpen(writer, localContext, modelForm); + Iterator<ModelFormField> innerDisplayHyperlinkFieldsBeginIter = innerDisplayHyperlinkFieldsBegin.iterator(); + Map<String, Integer> fieldCount = new HashMap<String, Integer>(); + while (innerDisplayHyperlinkFieldsBeginIter.hasNext()) { + ModelFormField modelFormField = innerDisplayHyperlinkFieldsBeginIter.next(); + if (fieldCount.containsKey(modelFormField.getFieldName())) { + fieldCount.put(modelFormField.getFieldName(), fieldCount.get(modelFormField.getFieldName()) + 1); + } else { + fieldCount.put(modelFormField.getFieldName(), 1); + } + } + + if (modelForm.getGroupColumns()) { + // do the first part of display and hyperlink fields + Iterator<ModelFormField> innerDisplayHyperlinkFieldIter = innerDisplayHyperlinkFieldsBegin.iterator(); + while (innerDisplayHyperlinkFieldIter.hasNext()) { + boolean cellOpen = false; + ModelFormField modelFormField = innerDisplayHyperlinkFieldIter.next(); + // span columns only if this is the last column in the row (not just in this first list) + if (fieldCount.get(modelFormField.getName()) < 2) { + if ((innerDisplayHyperlinkFieldIter.hasNext() || numOfCells > innerDisplayHyperlinkFieldsBegin.size())) { + formStringRenderer.renderFormatItemRowCellOpen(writer, localContext, modelForm, modelFormField, 1); + } else { + formStringRenderer.renderFormatItemRowCellOpen(writer, localContext, modelForm, modelFormField, + numOfColumnsToSpan); + } + cellOpen = true; + } + if ((!"list".equals(modelForm.getType()) && !"multi".equals(modelForm.getType())) + || modelFormField.shouldUse(localContext)) { + if ((fieldCount.get(modelFormField.getName()) > 1)) { + if ((innerDisplayHyperlinkFieldIter.hasNext() || numOfCells > innerDisplayHyperlinkFieldsBegin.size())) { + formStringRenderer.renderFormatItemRowCellOpen(writer, localContext, modelForm, modelFormField, 1); + } else { + formStringRenderer.renderFormatItemRowCellOpen(writer, localContext, modelForm, modelFormField, + numOfColumnsToSpan); + } + cellOpen = true; + } + modelFormField.renderFieldString(writer, localContext, formStringRenderer); + } + if (cellOpen) { + formStringRenderer.renderFormatItemRowCellClose(writer, localContext, modelForm, modelFormField); + } + } + + // The form cell is rendered only if there is at least an input field + if (innerFormFields.size() > 0) { + // render the "form" cell + formStringRenderer.renderFormatItemRowFormCellOpen(writer, localContext, modelForm); // TODO: colspan + + if (formPerItem) { + formStringRenderer.renderFormOpen(writer, localContext, modelForm); + } + + // do all of the hidden fields... + this.renderHiddenIgnoredFields(writer, localContext, formStringRenderer, hiddenIgnoredFieldList); + + Iterator<ModelFormField> innerFormFieldIter = innerFormFields.iterator(); + while (innerFormFieldIter.hasNext()) { + ModelFormField modelFormField = innerFormFieldIter.next(); + if (modelForm.getSeparateColumns() || modelFormField.getSeparateColumn()) { + formStringRenderer.renderFormatItemRowCellOpen(writer, localContext, modelForm, modelFormField, 1); + } + // render field widget + if ((!"list".equals(modelForm.getType()) && !"multi".equals(modelForm.getType())) + || modelFormField.shouldUse(localContext)) { + modelFormField.renderFieldString(writer, localContext, formStringRenderer); + } + + if (modelForm.getSeparateColumns() || modelFormField.getSeparateColumn()) { + formStringRenderer.renderFormatItemRowCellClose(writer, localContext, modelForm, modelFormField); + } + } + + if (formPerItem) { + formStringRenderer.renderFormClose(writer, localContext, modelForm); + } + + formStringRenderer.renderFormatItemRowFormCellClose(writer, localContext, modelForm); + } + + // render the rest of the display/hyperlink fields + innerDisplayHyperlinkFieldIter = innerDisplayHyperlinkFieldsEnd.iterator(); + while (innerDisplayHyperlinkFieldIter.hasNext()) { + ModelFormField modelFormField = innerDisplayHyperlinkFieldIter.next(); + // span columns only if this is the last column in the row + if (innerDisplayHyperlinkFieldIter.hasNext()) { + formStringRenderer.renderFormatItemRowCellOpen(writer, localContext, modelForm, modelFormField, 1); + } else { + formStringRenderer.renderFormatItemRowCellOpen(writer, localContext, modelForm, modelFormField, + numOfColumnsToSpan); + } + if ((!"list".equals(modelForm.getType()) && !"multi".equals(modelForm.getType())) + || modelFormField.shouldUse(localContext)) { + modelFormField.renderFieldString(writer, localContext, formStringRenderer); + } + formStringRenderer.renderFormatItemRowCellClose(writer, localContext, modelForm, modelFormField); + } + } else { + // do all of the hidden fields... + this.renderHiddenIgnoredFields(writer, localContext, formStringRenderer, hiddenIgnoredFieldList); + + Iterator<ModelFormField> mainFieldIter = mainFieldList.iterator(); + while (mainFieldIter.hasNext()) { + ModelFormField modelFormField = mainFieldIter.next(); + + // don't do any header for hidden or ignored fields inside this loop + FieldInfo fieldInfo = modelFormField.getFieldInfo(); + if (fieldInfo.getFieldType() == FieldInfo.HIDDEN + || fieldInfo.getFieldType() == FieldInfo.IGNORED) { + continue; + } + + // span columns only if this is the last column in the row + if (mainFieldIter.hasNext()) { + formStringRenderer.renderFormatItemRowCellOpen(writer, localContext, modelForm, modelFormField, 1); + } else { + formStringRenderer.renderFormatItemRowCellOpen(writer, localContext, modelForm, modelFormField, + numOfColumnsToSpan); + } + if ((!"list".equals(modelForm.getType()) && !"multi".equals(modelForm.getType())) + || modelFormField.shouldUse(localContext)) { + modelFormField.renderFieldString(writer, localContext, formStringRenderer); + } + formStringRenderer.renderFormatItemRowCellClose(writer, localContext, modelForm, modelFormField); + } + } + + // render row formatting close + formStringRenderer.renderFormatItemRowClose(writer, localContext, modelForm); + } + + private void renderItemRows(Appendable writer, Map<String, Object> context, FormStringRenderer formStringRenderer, + boolean formPerItem, int numOfColumns) throws IOException { + String lookupName = modelForm.getListName(); + if (UtilValidate.isEmpty(lookupName)) { + Debug.logError("No value for list or iterator name found.", module); + return; + } + Object obj = context.get(lookupName); + if (obj == null) { + if (Debug.verboseOn()) + Debug.logVerbose("No object for list or iterator name [" + lookupName + "] found, so not rendering rows.", module); + return; + } + // if list is empty, do not render rows + Iterator<?> iter = null; + if (obj instanceof Iterator<?>) { + iter = (Iterator<?>) obj; + } else if (obj instanceof List<?>) { + iter = ((List<?>) obj).listIterator(); + } + + // set low and high index + Paginator.getListLimits(modelForm, context, obj); + + int listSize = ((Integer) context.get("listSize")).intValue(); + int lowIndex = ((Integer) context.get("lowIndex")).intValue(); + int highIndex = ((Integer) context.get("highIndex")).intValue(); + + // we're passed a subset of the list, so use (0, viewSize) range + if (modelForm.isOverridenListSize()) { + lowIndex = 0; + highIndex = ((Integer) context.get("viewSize")).intValue(); + } + + if (iter != null) { + // render item rows + int itemIndex = -1; + Object item = null; + context.put("wholeFormContext", context); + Map<String, Object> previousItem = new HashMap<String, Object>(); + while ((item = safeNext(iter)) != null) { + itemIndex++; + if (itemIndex >= highIndex) { + break; + } + + // TODO: this is a bad design, for EntityListIterators we should skip to the lowIndex and go from there, MUCH more efficient... + if (itemIndex < lowIndex) { + continue; + } + + // reset/remove the BshInterpreter now as well as later because chances are there is an interpreter at this level of the stack too + this.resetBshInterpreter(context); + + Map<String, Object> itemMap = UtilGenerics.checkMap(item); + MapStack<String> localContext = MapStack.create(context); + if (UtilValidate.isNotEmpty(modelForm.getListEntryName())) { + localContext.put(modelForm.getListEntryName(), item); + } else { + if (itemMap instanceof GenericEntity) { + // Rendering code might try to modify the GenericEntity instance, + // so we make a copy of it. + Map<String, Object> genericEntityClone = UtilGenerics.cast(((GenericEntity) itemMap).clone()); + localContext.push(genericEntityClone); + } else { + localContext.push(itemMap); + } + } + + // reset/remove the BshInterpreter now as well as later because chances are there is an interpreter at this level of the stack too + this.resetBshInterpreter(localContext); + localContext.push(); + localContext.put("previousItem", previousItem); + previousItem = new HashMap<String, Object>(); + previousItem.putAll(itemMap); + + AbstractModelAction.runSubActions(modelForm.getRowActions(), localContext); + + localContext.put("itemIndex", Integer.valueOf(itemIndex - lowIndex)); + if (UtilValidate.isNotEmpty(context.get("renderFormSeqNumber"))) { + localContext.put("formUniqueId", "_" + context.get("renderFormSeqNumber")); + } + + this.resetBshInterpreter(localContext); + + if (Debug.verboseOn()) + Debug.logVerbose("In form got another row, context is: " + localContext, module); + + // Check to see if there is a field, same name and same use-when (could come from extended form) + List<ModelFormField> tempFieldList = new LinkedList<ModelFormField>(); + tempFieldList.addAll(modelForm.getFieldList()); + for (int j = 0; j < tempFieldList.size(); j++) { + ModelFormField modelFormField = tempFieldList.get(j); + if (!modelFormField.isUseWhenEmpty()) { + boolean shouldUse1 = modelFormField.shouldUse(localContext); + for (int i = j + 1; i < tempFieldList.size(); i++) { + ModelFormField curField = tempFieldList.get(i); + if (curField.getName() != null && curField.getName().equals(modelFormField.getName())) { + boolean shouldUse2 = curField.shouldUse(localContext); + if (shouldUse1 == shouldUse2) { + tempFieldList.remove(i--); + } + } else { + continue; + } + } + } + } + + // Each single item is rendered in one or more rows if its fields have + // different "position" attributes. All the fields with the same position + // are rendered in the same row. + // The default position is 1, and represents the main row: + // it contains the fields that are in the list header (columns). + // The positions lower than 1 are rendered in rows before the main one; + // positions higher than 1 are rendered after the main one. + + // We get a sorted (by position, ascending) set of lists; + // each list contains all the fields with that position. + Collection<List<ModelFormField>> fieldListsByPosition = this.getFieldListsByPosition(tempFieldList); + //List hiddenIgnoredFieldList = getHiddenIgnoredFields(localContext, null, tempFieldList); + for (List<ModelFormField> fieldListByPosition : fieldListsByPosition) { + // For each position (the subset of fields with the same position attribute) + // we have two phases: preprocessing and rendering + + List<ModelFormField> innerDisplayHyperlinkFieldsBegin = new LinkedList<ModelFormField>(); + List<ModelFormField> innerFormFields = new LinkedList<ModelFormField>(); + List<ModelFormField> innerDisplayHyperlinkFieldsEnd = new LinkedList<ModelFormField>(); + + // Preprocessing: + // all the form fields are evaluated and the ones that will + // appear in the form are put into three separate lists: + // - hyperlink fields that will appear at the beginning of the row + // - fields of other types + // - hyperlink fields that will appear at the end of the row + Iterator<ModelFormField> innerDisplayHyperlinkFieldIter = fieldListByPosition.iterator(); + int currentPosition = 1; + while (innerDisplayHyperlinkFieldIter.hasNext()) { + ModelFormField modelFormField = innerDisplayHyperlinkFieldIter.next(); + FieldInfo fieldInfo = modelFormField.getFieldInfo(); + + // don't do any header for hidden or ignored fields + if (fieldInfo.getFieldType() == FieldInfo.HIDDEN + || fieldInfo.getFieldType() == FieldInfo.IGNORED) { + continue; + } + + if (fieldInfo.getFieldType() != FieldInfo.DISPLAY + && fieldInfo.getFieldType() != FieldInfo.DISPLAY_ENTITY + && fieldInfo.getFieldType() != FieldInfo.HYPERLINK) { + // okay, now do the form cell + break; + } + + // if this is a list or multi form don't skip here because we don't want to skip the table cell, will skip the actual field later + if (!"list".equals(modelForm.getType()) && !"multi".equals(modelForm.getType()) + && !modelFormField.shouldUse(localContext)) { + continue; + } + innerDisplayHyperlinkFieldsBegin.add(modelFormField); + currentPosition = modelFormField.getPosition(); + } + Iterator<ModelFormField> innerFormFieldIter = fieldListByPosition.iterator(); + while (innerFormFieldIter.hasNext()) { + ModelFormField modelFormField = innerFormFieldIter.next(); + FieldInfo fieldInfo = modelFormField.getFieldInfo(); + + // don't do any header for hidden or ignored fields + if (fieldInfo.getFieldType() == FieldInfo.HIDDEN + || fieldInfo.getFieldType() == FieldInfo.IGNORED) { + continue; + } + + // skip all of the display/hyperlink fields + if (fieldInfo.getFieldType() == FieldInfo.DISPLAY + || fieldInfo.getFieldType() == FieldInfo.DISPLAY_ENTITY + || fieldInfo.getFieldType() == FieldInfo.HYPERLINK) { + continue; + } + + // if this is a list or multi form don't skip here because we don't want to skip the table cell, will skip the actual field later + if (!"list".equals(modelForm.getType()) && !"multi".equals(modelForm.getType()) + && !modelFormField.shouldUse(localContext)) { + continue; + } + innerFormFields.add(modelFormField); + currentPosition = modelFormField.getPosition(); + } + while (innerDisplayHyperlinkFieldIter.hasNext()) { + ModelFormField modelFormField = innerDisplayHyperlinkFieldIter.next(); + FieldInfo fieldInfo = modelFormField.getFieldInfo(); + + // don't do any header for hidden or ignored fields + if (fieldInfo.getFieldType() == FieldInfo.HIDDEN + || fieldInfo.getFieldType() == FieldInfo.IGNORED) { + continue; + } + + // skip all non-display and non-hyperlink fields + if (fieldInfo.getFieldType() != FieldInfo.DISPLAY + && fieldInfo.getFieldType() != FieldInfo.DISPLAY_ENTITY + && fieldInfo.getFieldType() != FieldInfo.HYPERLINK) { + continue; + } + + // if this is a list or multi form don't skip here because we don't want to skip the table cell, will skip the actual field later + if (!"list".equals(modelForm.getType()) && !"multi".equals(modelForm.getType()) + && !modelFormField.shouldUse(localContext)) { + continue; + } + innerDisplayHyperlinkFieldsEnd.add(modelFormField); + currentPosition = modelFormField.getPosition(); + } + List<ModelFormField> hiddenIgnoredFieldList = getHiddenIgnoredFields(localContext, null, tempFieldList, + currentPosition); + + // Rendering: + // the fields in the three lists created in the preprocessing phase + // are now rendered: this will create a visual representation + // of one row (for the current position). + if (innerDisplayHyperlinkFieldsBegin.size() > 0 || innerFormFields.size() > 0 + || innerDisplayHyperlinkFieldsEnd.size() > 0) { + this.renderItemRow(writer, localContext, formStringRenderer, formPerItem, hiddenIgnoredFieldList, + innerDisplayHyperlinkFieldsBegin, innerFormFields, innerDisplayHyperlinkFieldsEnd, + fieldListByPosition, currentPosition, numOfColumns); + } + } // iteration on positions + } // iteration on items + + // reduce the highIndex if number of items falls short + if ((itemIndex + 1) < highIndex) { + highIndex = itemIndex + 1; + // if list size is overridden, use full listSize + context.put("highIndex", Integer.valueOf(modelForm.isOverridenListSize() ? listSize : highIndex)); + } + context.put("actualPageSize", Integer.valueOf(highIndex - lowIndex)); + + if (iter instanceof EntityListIterator) { + try { + ((EntityListIterator) iter).close(); + } catch (GenericEntityException e) { + Debug.logError(e, "Error closing list form render EntityListIterator: " + e.toString(), module); + } + } + } + } + + private void renderListFormString(Appendable writer, Map<String, Object> context, + int positions) throws IOException { + // render list/tabular type forms + + // prepare the items iterator and compute the pagination parameters + Paginator.preparePager(modelForm, context); + + // render formatting wrapper open + formStringRenderer.renderFormatListWrapperOpen(writer, context, modelForm); + + int numOfColumns = 0; + // ===== render header row ===== + if (!modelForm.getHideHeader()) { + numOfColumns = this.renderHeaderRow(writer, context); + } + + // ===== render the item rows ===== + this.renderItemRows(writer, context, formStringRenderer, true, numOfColumns); + + // render formatting wrapper close + formStringRenderer.renderFormatListWrapperClose(writer, context, modelForm); + + } + + private void renderMultiFormString(Appendable writer, Map<String, Object> context, + int positions) throws IOException { + if (!modelForm.getSkipStart()) { + formStringRenderer.renderFormOpen(writer, context, modelForm); + } + + // prepare the items iterator and compute the pagination parameters + Paginator.preparePager(modelForm, context); + + // render formatting wrapper open + formStringRenderer.renderFormatListWrapperOpen(writer, context, modelForm); + + int numOfColumns = 0; + // ===== render header row ===== + if (!modelForm.getHideHeader()) { + numOfColumns = this.renderHeaderRow(writer, context); + } + + // ===== render the item rows ===== + this.renderItemRows(writer, context, formStringRenderer, false, numOfColumns); + + formStringRenderer.renderFormatListWrapperClose(writer, context, modelForm); + + if (!modelForm.getSkipEnd()) { + formStringRenderer.renderMultiFormClose(writer, context, modelForm); + } + + } + + private void renderSingleFormString(Appendable writer, Map<String, Object> context, + int positions) throws IOException { + List<ModelFormField> tempFieldList = new LinkedList<ModelFormField>(); + tempFieldList.addAll(modelForm.getFieldList()); + + // Check to see if there is a field, same name and same use-when (could come from extended form) + for (int j = 0; j < tempFieldList.size(); j++) { + ModelFormField modelFormField = tempFieldList.get(j); + if (modelForm.getUseWhenFields().contains(modelFormField.getName())) { + boolean shouldUse1 = modelFormField.shouldUse(context); + for (int i = j + 1; i < tempFieldList.size(); i++) { + ModelFormField curField = tempFieldList.get(i); + if (curField.getName() != null && curField.getName().equals(modelFormField.getName())) { + boolean shouldUse2 = curField.shouldUse(context); + if (shouldUse1 == shouldUse2) { + tempFieldList.remove(i--); + } + } else { + continue; + } + } + } + } + + Set<String> alreadyRendered = new TreeSet<String>(); + FieldGroup lastFieldGroup = null; + // render form open + if (!modelForm.getSkipStart()) + formStringRenderer.renderFormOpen(writer, context, modelForm); + + // render all hidden & ignored fields + List<ModelFormField> hiddenIgnoredFieldList = this.getHiddenIgnoredFields(context, alreadyRendered, tempFieldList, -1); + this.renderHiddenIgnoredFields(writer, context, formStringRenderer, hiddenIgnoredFieldList); + + // render formatting wrapper open + // This should be covered by fieldGroup.renderStartString + //formStringRenderer.renderFormatSingleWrapperOpen(writer, context, this); + + // render each field row, except hidden & ignored rows + Iterator<ModelFormField> fieldIter = tempFieldList.iterator(); + ModelFormField lastFormField = null; + ModelFormField currentFormField = null; + ModelFormField nextFormField = null; + if (fieldIter.hasNext()) { + currentFormField = fieldIter.next(); + } + if (fieldIter.hasNext()) { + nextFormField = fieldIter.next(); + } + + FieldGroup currentFieldGroup = null; + String currentFieldGroupName = null; + String lastFieldGroupName = null; + if (currentFormField != null) { + currentFieldGroup = (FieldGroup) modelForm.getFieldGroupMap().get(currentFormField.getFieldName()); + if (currentFieldGroup == null) { + currentFieldGroup = modelForm.getDefaultFieldGroup(); + } + if (currentFieldGroup != null) { + currentFieldGroupName = currentFieldGroup.getId(); + } + } + + boolean isFirstPass = true; + boolean haveRenderedOpenFieldRow = false; + while (currentFormField != null) { + // do the check/get next stuff at the beginning so we can still use the continue stuff easily + // don't do it on the first pass though... + if (isFirstPass) { + isFirstPass = false; + List<FieldGroupBase> inbetweenList = getInbetweenList(lastFieldGroup, currentFieldGroup); + for (FieldGroupBase obj : inbetweenList) { + if (obj instanceof ModelForm.Banner) { + ((ModelForm.Banner) obj).renderString(writer, context, formStringRenderer); + } + } + if (currentFieldGroup != null && (lastFieldGroup == null || !lastFieldGroupName.equals(currentFieldGroupName))) { + currentFieldGroup.renderStartString(writer, context, formStringRenderer); + lastFieldGroup = currentFieldGroup; + } + } else { + if (fieldIter.hasNext()) { + // at least two loops left + lastFormField = currentFormField; + currentFormField = nextFormField; + nextFormField = fieldIter.next(); + } else if (nextFormField != null) { + // okay, just one loop left + lastFormField = currentFormField; + currentFormField = nextFormField; + nextFormField = null; + } else { + // at the end... + lastFormField = currentFormField; + currentFormField = null; + // nextFormField is already null + break; + } + currentFieldGroup = null; + if (currentFormField != null) { + currentFieldGroup = (FieldGroup) modelForm.getFieldGroupMap().get(currentFormField.getName()); + } + if (currentFieldGroup == null) { + currentFieldGroup = modelForm.getDefaultFieldGroup(); + } + currentFieldGroupName = currentFieldGroup.getId(); + + if (lastFieldGroup != null) { + lastFieldGroupName = lastFieldGroup.getId(); + if (!lastFieldGroupName.equals(currentFieldGroupName)) { + if (haveRenderedOpenFieldRow) { + formStringRenderer.renderFormatFieldRowClose(writer, context, modelForm); + haveRenderedOpenFieldRow = false; + } + lastFieldGroup.renderEndString(writer, context, formStringRenderer); + + List<FieldGroupBase> inbetweenList = getInbetweenList(lastFieldGroup, currentFieldGroup); + for (FieldGroupBase obj : inbetweenList) { + if (obj instanceof ModelForm.Banner) { + ((ModelForm.Banner) obj).renderString(writer, context, formStringRenderer); + } + } + } + } + + if (lastFieldGroup == null || !lastFieldGroupName.equals(currentFieldGroupName)) { + currentFieldGroup.renderStartString(writer, context, formStringRenderer); + lastFieldGroup = currentFieldGroup; + } + } + + FieldInfo fieldInfo = currentFormField.getFieldInfo(); + if (fieldInfo.getFieldType() == FieldInfo.HIDDEN + || fieldInfo.getFieldType() == FieldInfo.IGNORED) { + continue; + } + if (alreadyRendered.contains(currentFormField.getName())) { + continue; + } + //Debug.logInfo("In single form evaluating use-when for field " + currentFormField.getName() + ": " + currentFormField.getUseWhen(), module); + if (!currentFormField.shouldUse(context)) { + if (UtilValidate.isNotEmpty(lastFormField)) { + currentFormField = lastFormField; + } + continue; + } + alreadyRendered.add(currentFormField.getName()); + if (focusFieldName.isEmpty()) { + if (fieldInfo.getFieldType() != FieldInfo.DISPLAY && fieldInfo.getFieldType() != FieldInfo.HIDDEN + && fieldInfo.getFieldType() != FieldInfo.DISPLAY_ENTITY + && fieldInfo.getFieldType() != FieldInfo.IGNORED + && fieldInfo.getFieldType() != FieldInfo.IMAGE) { + focusFieldName = currentFormField.getName(); + context.put(modelForm.getName().concat(".focusFieldName"), focusFieldName); + } + } + + boolean stayingOnRow = false; + if (lastFormField != null) { + if (lastFormField.getPosition() >= currentFormField.getPosition()) { + // moving to next row + stayingOnRow = false; + } else { + // staying on same row + stayingOnRow = true; + } + } + + int positionSpan = 1; + Integer nextPositionInRow = null; + if (nextFormField != null) { + if (nextFormField.getPosition() > currentFormField.getPosition()) { + positionSpan = nextFormField.getPosition() - currentFormField.getPosition() - 1; + nextPositionInRow = Integer.valueOf(nextFormField.getPosition()); + } else { + positionSpan = positions - currentFormField.getPosition(); + if (!stayingOnRow && nextFormField.getPosition() > 1) { + // TODO: here is a weird case where it is setup such + //that the first position(s) in the row are skipped + // not sure what to do about this right now... + } + } + } + + if (stayingOnRow) { + // no spacer cell, might add later though... + //formStringRenderer.renderFormatFieldRowSpacerCell(writer, context, currentFormField); + } else { + if (haveRenderedOpenFieldRow) { + // render row formatting close + formStringRenderer.renderFormatFieldRowClose(writer, context, modelForm); + haveRenderedOpenFieldRow = false; + } + + // render row formatting open + formStringRenderer.renderFormatFieldRowOpen(writer, context, modelForm); + haveRenderedOpenFieldRow = true; + } + + // + // It must be a row open before rendering a field. If not, open it + // + if (!haveRenderedOpenFieldRow) { + formStringRenderer.renderFormatFieldRowOpen(writer, context, modelForm); + haveRenderedOpenFieldRow = true; + } + + // render title formatting open + formStringRenderer.renderFormatFieldRowTitleCellOpen(writer, context, currentFormField); + + // render title (unless this is a submit or a reset field) + if (fieldInfo.getFieldType() != FieldInfo.SUBMIT + && fieldInfo.getFieldType() != FieldInfo.RESET) { + formStringRenderer.renderFieldTitle(writer, context, currentFormField); + } else { + formStringRenderer.renderFormatEmptySpace(writer, context, modelForm); + } + + // render title formatting close + formStringRenderer.renderFormatFieldRowTitleCellClose(writer, context, currentFormField); + + // render separator + formStringRenderer.renderFormatFieldRowSpacerCell(writer, context, currentFormField); + + // render widget formatting open + formStringRenderer.renderFormatFieldRowWidgetCellOpen(writer, context, currentFormField, positions, positionSpan, + nextPositionInRow); + + // render widget + currentFormField.renderFieldString(writer, context, formStringRenderer); + + // render widget formatting close + formStringRenderer.renderFormatFieldRowWidgetCellClose(writer, context, currentFormField, positions, positionSpan, + nextPositionInRow); + + } + // render row formatting close after the end if needed + if (haveRenderedOpenFieldRow) { + formStringRenderer.renderFormatFieldRowClose(writer, context, modelForm); + } + + if (lastFieldGroup != null) { + lastFieldGroup.renderEndString(writer, context, formStringRenderer); + } + // render formatting wrapper close + // should be handled by renderEndString + //formStringRenderer.renderFormatSingleWrapperClose(writer, context, this); + + // render form close + if (!modelForm.getSkipEnd()) + formStringRenderer.renderFormClose(writer, context, modelForm); + + } + + private void resetBshInterpreter(Map<String, Object> context) { + context.remove("bshInterpreter"); + } + + private static <X> X safeNext(Iterator<X> iterator) { + try { + return iterator.next(); + } catch (NoSuchElementException e) { + return null; + } + } +} Added: ofbiz/trunk/framework/widget/src/org/ofbiz/widget/renderer/FormStringRenderer.java URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/widget/src/org/ofbiz/widget/renderer/FormStringRenderer.java?rev=1652852&view=auto ============================================================================== --- ofbiz/trunk/framework/widget/src/org/ofbiz/widget/renderer/FormStringRenderer.java (added) +++ ofbiz/trunk/framework/widget/src/org/ofbiz/widget/renderer/FormStringRenderer.java Sun Jan 18 21:03:40 2015 @@ -0,0 +1,99 @@ +/******************************************************************************* + * 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.renderer; + +import java.io.IOException; +import java.util.Map; + +import org.ofbiz.widget.model.ModelForm; +import org.ofbiz.widget.model.ModelFormField; + +/** + * Widget Library - Form String Renderer interface. + */ +public interface FormStringRenderer { + public void renderDisplayField(Appendable writer, Map<String, Object> context, ModelFormField.DisplayField displayField) throws IOException; + public void renderHyperlinkField(Appendable writer, Map<String, Object> context, ModelFormField.HyperlinkField hyperlinkField) throws IOException; + + public void renderTextField(Appendable writer, Map<String, Object> context, ModelFormField.TextField textField) throws IOException; + public void renderTextareaField(Appendable writer, Map<String, Object> context, ModelFormField.TextareaField textareaField) throws IOException; + public void renderDateTimeField(Appendable writer, Map<String, Object> context, ModelFormField.DateTimeField dateTimeField) throws IOException; + + public void renderDropDownField(Appendable writer, Map<String, Object> context, ModelFormField.DropDownField dropDownField) throws IOException; + public void renderCheckField(Appendable writer, Map<String, Object> context, ModelFormField.CheckField checkField) throws IOException; + public void renderRadioField(Appendable writer, Map<String, Object> context, ModelFormField.RadioField radioField) throws IOException; + + public void renderSubmitField(Appendable writer, Map<String, Object> context, ModelFormField.SubmitField submitField) throws IOException; + public void renderResetField(Appendable writer, Map<String, Object> context, ModelFormField.ResetField resetField) throws IOException; + + public void renderHiddenField(Appendable writer, Map<String, Object> context, ModelFormField modelFormField, String value) throws IOException; + public void renderHiddenField(Appendable writer, Map<String, Object> context, ModelFormField.HiddenField hiddenField) throws IOException; + public void renderIgnoredField(Appendable writer, Map<String, Object> context, ModelFormField.IgnoredField ignoredField) throws IOException; + + public void renderFieldTitle(Appendable writer, Map<String, Object> context, ModelFormField modelFormField) throws IOException; + public void renderSingleFormFieldTitle(Appendable writer, Map<String, Object> context, ModelFormField modelFormField) throws IOException; + + public void renderFormOpen(Appendable writer, Map<String, Object> context, ModelForm modelForm) throws IOException; + public void renderFormClose(Appendable writer, Map<String, Object> context, ModelForm modelForm) throws IOException; + public void renderMultiFormClose(Appendable writer, Map<String, Object> context, ModelForm modelForm) throws IOException; + + public void renderFormatListWrapperOpen(Appendable writer, Map<String, Object> context, ModelForm modelForm) throws IOException; + public void renderFormatListWrapperClose(Appendable writer, Map<String, Object> context, ModelForm modelForm) throws IOException; + + public void renderFormatHeaderRowOpen(Appendable writer, Map<String, Object> context, ModelForm modelForm) throws IOException; + public void renderFormatHeaderRowClose(Appendable writer, Map<String, Object> context, ModelForm modelForm) throws IOException; + public void renderFormatHeaderRowCellOpen(Appendable writer, Map<String, Object> context, ModelForm modelForm, ModelFormField modelFormField, int positionSpan) throws IOException; + public void renderFormatHeaderRowCellClose(Appendable writer, Map<String, Object> context, ModelForm modelForm, ModelFormField modelFormField) throws IOException; + + public void renderFormatHeaderRowFormCellOpen(Appendable writer, Map<String, Object> context, ModelForm modelForm) throws IOException; + public void renderFormatHeaderRowFormCellClose(Appendable writer, Map<String, Object> context, ModelForm modelForm) throws IOException; + public void renderFormatHeaderRowFormCellTitleSeparator(Appendable writer, Map<String, Object> context, ModelForm modelForm, ModelFormField modelFormField, boolean isLast) throws IOException; + + public void renderFormatItemRowOpen(Appendable writer, Map<String, Object> context, ModelForm modelForm) throws IOException; + public void renderFormatItemRowClose(Appendable writer, Map<String, Object> context, ModelForm modelForm) throws IOException; + public void renderFormatItemRowCellOpen(Appendable writer, Map<String, Object> context, ModelForm modelForm, ModelFormField modelFormField, int positionSpan) throws IOException; + public void renderFormatItemRowCellClose(Appendable writer, Map<String, Object> context, ModelForm modelForm, ModelFormField modelFormField) throws IOException; + public void renderFormatItemRowFormCellOpen(Appendable writer, Map<String, Object> context, ModelForm modelForm) throws IOException; + public void renderFormatItemRowFormCellClose(Appendable writer, Map<String, Object> context, ModelForm modelForm) throws IOException; + + public void renderFormatSingleWrapperOpen(Appendable writer, Map<String, Object> context, ModelForm modelForm) throws IOException; + public void renderFormatSingleWrapperClose(Appendable writer, Map<String, Object> context, ModelForm modelForm) throws IOException; + + public void renderFormatFieldRowOpen(Appendable writer, Map<String, Object> context, ModelForm modelForm) throws IOException; + public void renderFormatFieldRowClose(Appendable writer, Map<String, Object> context, ModelForm modelForm) throws IOException; + public void renderFormatFieldRowTitleCellOpen(Appendable writer, Map<String, Object> context, ModelFormField modelFormField) throws IOException; + public void renderFormatFieldRowTitleCellClose(Appendable writer, Map<String, Object> context, ModelFormField modelFormField) throws IOException; + public void renderFormatFieldRowSpacerCell(Appendable writer, Map<String, Object> context, ModelFormField modelFormField) throws IOException; + public void renderFormatFieldRowWidgetCellOpen(Appendable writer, Map<String, Object> context, ModelFormField modelFormField, int positions, int positionSpan, Integer nextPositionInRow) throws IOException; + public void renderFormatFieldRowWidgetCellClose(Appendable writer, Map<String, Object> context, ModelFormField modelFormField, int positions, int positionSpan, Integer nextPositionInRow) throws IOException; + + public void renderFormatEmptySpace(Appendable writer, Map<String, Object> context, ModelForm modelForm) throws IOException; + + public void renderTextFindField(Appendable writer, Map<String, Object> context, ModelFormField.TextFindField textField) throws IOException; + public void renderDateFindField(Appendable writer, Map<String, Object> context, ModelFormField.DateFindField textField) throws IOException; + public void renderRangeFindField(Appendable writer, Map<String, Object> context, ModelFormField.RangeFindField textField) throws IOException; + public void renderLookupField(Appendable writer, Map<String, Object> context, ModelFormField.LookupField textField) throws IOException; + public void renderFileField(Appendable writer, Map<String, Object> context, ModelFormField.FileField textField) throws IOException; + public void renderPasswordField(Appendable writer, Map<String, Object> context, ModelFormField.PasswordField textField) throws IOException; + public void renderImageField(Appendable writer, Map<String, Object> context, ModelFormField.ImageField textField) throws IOException; + public void renderBanner(Appendable writer, Map<String, Object> context, ModelForm.Banner banner) throws IOException; + public void renderContainerFindField(Appendable writer, Map<String, Object> context, ModelFormField.ContainerField containerField) throws IOException; + public void renderFieldGroupOpen(Appendable writer, Map<String, Object> context, ModelForm.FieldGroup fieldGroup) throws IOException; + public void renderFieldGroupClose(Appendable writer, Map<String, Object> context, ModelForm.FieldGroup fieldGroup) throws IOException; +} Added: ofbiz/trunk/framework/widget/src/org/ofbiz/widget/renderer/MenuStringRenderer.java URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/widget/src/org/ofbiz/widget/renderer/MenuStringRenderer.java?rev=1652852&view=auto ============================================================================== --- ofbiz/trunk/framework/widget/src/org/ofbiz/widget/renderer/MenuStringRenderer.java (added) +++ ofbiz/trunk/framework/widget/src/org/ofbiz/widget/renderer/MenuStringRenderer.java Sun Jan 18 21:03:40 2015 @@ -0,0 +1,41 @@ +/******************************************************************************* + * 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") throws IOException ; 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.renderer; + +import java.io.IOException; +import java.util.Map; + +import org.ofbiz.widget.model.CommonWidgetModels.Image; +import org.ofbiz.widget.model.ModelMenu; +import org.ofbiz.widget.model.ModelMenuItem; + + +/** + * Widget Library - Form String Renderer interface + */ +public interface MenuStringRenderer { + public void renderMenuItem(Appendable writer, Map<String, Object> context, ModelMenuItem menuItem) throws IOException ; + public void renderMenuOpen(Appendable writer, Map<String, Object> context, ModelMenu menu) throws IOException ; + public void renderMenuClose(Appendable writer, Map<String, Object> context, ModelMenu menu) throws IOException ; + public void renderFormatSimpleWrapperOpen(Appendable writer, Map<String, Object> context, ModelMenu menu) throws IOException ; + public void renderFormatSimpleWrapperClose(Appendable writer, Map<String, Object> context, ModelMenu menu) throws IOException ; + public void renderFormatSimpleWrapperRows(Appendable writer, Map<String, Object> context, Object menu) throws IOException ; + public void renderLink(Appendable writer, Map<String, Object> context, ModelMenuItem.MenuLink link) throws IOException ; + public void renderImage(Appendable writer, Map<String, Object> context, Image image) throws IOException ; +} Added: ofbiz/trunk/framework/widget/src/org/ofbiz/widget/renderer/MenuWrapTransform.java URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/widget/src/org/ofbiz/widget/renderer/MenuWrapTransform.java?rev=1652852&view=auto ============================================================================== --- ofbiz/trunk/framework/widget/src/org/ofbiz/widget/renderer/MenuWrapTransform.java (added) +++ ofbiz/trunk/framework/widget/src/org/ofbiz/widget/renderer/MenuWrapTransform.java Sun Jan 18 21:03:40 2015 @@ -0,0 +1,200 @@ +/******************************************************************************* + * 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.renderer; + +import java.io.IOException; +import java.io.Writer; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.ofbiz.base.util.Debug; +import org.ofbiz.base.util.GeneralException; +import org.ofbiz.base.util.UtilGenerics; +import org.ofbiz.base.util.UtilValidate; +import org.ofbiz.base.util.template.FreeMarkerWorker; +import org.ofbiz.entity.Delegator; +import org.ofbiz.entity.GenericValue; +import org.ofbiz.webapp.ftl.LoopWriter; +import org.ofbiz.widget.content.WidgetContentWorker; +import org.ofbiz.widget.renderer.html.HtmlMenuWrapper; + +import freemarker.core.Environment; +import freemarker.template.TemplateModelException; +import freemarker.template.TemplateTransformModel; +import freemarker.template.TransformControl; + +//import com.clarkware.profiler.Profiler; +/** + * MenuWrapTransform - a FreeMarker transform that allow the ModelMenu + * stuff to be used at the FM level. It can be used to add "function bars" + * to pages. + * + * Accepts the following arguments (all of which can alternatively be present in the template context): + * - List<Map<String, ? extends Object>> globalNodeTrail + * - String contentAssocPredicateId + * - String nullThruDatesOnly + * - String subDataResourceTypeId + * - String renderOnStart + * - String renderOnClose + * - String menuDefFile + * - String menuName + * - String menuWrapperClassName + * - String associatedContentId + * + * This is an interactive FreeMarker transform that allows the user to modify the contents that are placed within it. + */ +public class MenuWrapTransform implements TemplateTransformModel { + + public static final String module = MenuWrapTransform.class.getName(); + public static final String [] upSaveKeyNames = {"globalNodeTrail"}; + public static final String [] saveKeyNames = {"contentId", "subContentId", "subDataResourceTypeId", "mimeTypeId", "whenMap", "locale", "wrapTemplateId", "encloseWrapText", "nullThruDatesOnly", "renderOnStart", "renderOnClose", "menuDefFile", "menuName", "associatedContentId", "wrapperClassName"}; + + @SuppressWarnings("unchecked") + public Writer getWriter(final Writer out, Map args) { + final Environment env = Environment.getCurrentEnvironment(); + final Delegator delegator = FreeMarkerWorker.getWrappedObject("delegator", env); + final HttpServletRequest request = FreeMarkerWorker.getWrappedObject("request", env); + final HttpServletResponse response = FreeMarkerWorker.getWrappedObject("response", env); + final HttpSession session = FreeMarkerWorker.getWrappedObject("session", env); + + final GenericValue userLogin = FreeMarkerWorker.getWrappedObject("userLogin", env); + final Map<String, Object> templateCtx = FreeMarkerWorker.getWrappedObject("context", env); + + FreeMarkerWorker.getSiteParameters(request, templateCtx); + + final Map<String, Object> savedValuesUp = new HashMap<String, Object>(); + FreeMarkerWorker.saveContextValues(templateCtx, upSaveKeyNames, savedValuesUp); + + Map<String, Object> checkedArgs = UtilGenerics.checkMap(args); + FreeMarkerWorker.overrideWithArgs(templateCtx, checkedArgs); + //final String menuDefFile = (String)templateCtx.get("menuDefFile"); + //final String menuName = (String)templateCtx.get("menuName"); + //final String associatedContentId = (String)templateCtx.get("associatedContentId"); + List<Map<String, ? extends Object>> trail = UtilGenerics.checkList(templateCtx.get("globalNodeTrail")); + String contentAssocPredicateId = (String)templateCtx.get("contentAssocPredicateId"); + String strNullThruDatesOnly = (String)templateCtx.get("nullThruDatesOnly"); + Boolean nullThruDatesOnly = (strNullThruDatesOnly != null && strNullThruDatesOnly.equalsIgnoreCase("true")) ? Boolean.TRUE :Boolean.FALSE; + GenericValue val = null; + try { + if (WidgetContentWorker.contentWorker != null) { + val = WidgetContentWorker.contentWorker.getCurrentContentExt(delegator, trail, userLogin, templateCtx, nullThruDatesOnly, contentAssocPredicateId); + } else { + Debug.logError("Not rendering content, not ContentWorker found.", module); + } + } catch (GeneralException e) { + throw new RuntimeException("Error getting current content. " + e.toString()); + } + final GenericValue view = val; + + String dataResourceId = null; + try { + dataResourceId = (String) view.get("drDataResourceId"); + } catch (Exception e) { + dataResourceId = (String) view.get("dataResourceId"); + } + String subContentIdSub = (String) view.get("contentId"); + // This order is taken so that the dataResourceType can be overridden in the transform arguments. + String subDataResourceTypeId = (String)templateCtx.get("subDataResourceTypeId"); + if (UtilValidate.isEmpty(subDataResourceTypeId)) { + try { + subDataResourceTypeId = (String) view.get("drDataResourceTypeId"); + } catch (Exception e) { + // view may be "Content" + } + // TODO: If this value is still empty then it is probably necessary to get a value from + // the parent context. But it will already have one and it is the same context that is + // being passed. + } + // This order is taken so that the mimeType can be overridden in the transform arguments. + String mimeTypeId = null; + if (WidgetContentWorker.contentWorker != null) { + mimeTypeId = WidgetContentWorker.contentWorker.getMimeTypeIdExt(delegator, view, templateCtx); + } else { + Debug.logError("Not rendering content, not ContentWorker found.", module); + } + templateCtx.put("drDataResourceId", dataResourceId); + templateCtx.put("mimeTypeId", mimeTypeId); + templateCtx.put("dataResourceId", dataResourceId); + templateCtx.put("subContentIdSub", subContentIdSub); + templateCtx.put("subDataResourceTypeId", subDataResourceTypeId); + final Map<String, Object> savedValues = new HashMap<String, Object>(); + FreeMarkerWorker.saveContextValues(templateCtx, saveKeyNames, savedValues); + + final StringBuilder buf = new StringBuilder(); + + return new LoopWriter(out) { + + @Override + public int onStart() throws TemplateModelException, IOException { + String renderOnStart = (String)templateCtx.get("renderOnStart"); + if (renderOnStart != null && renderOnStart.equalsIgnoreCase("true")) { + renderMenu(); + } + return TransformControl.EVALUATE_BODY; + } + + @Override + public void write(char cbuf[], int off, int len) { + buf.append(cbuf, off, len); + } + + @Override + public void flush() throws IOException { + out.flush(); + } + + @Override + public void close() throws IOException { + FreeMarkerWorker.reloadValues(templateCtx, savedValues, env); + String wrappedContent = buf.toString(); + out.write(wrappedContent); + String renderOnClose = (String)templateCtx.get("renderOnClose"); + if (renderOnClose == null || !renderOnClose.equalsIgnoreCase("false")) { + renderMenu(); + } + FreeMarkerWorker.reloadValues(templateCtx, savedValuesUp, env); + } + + public void renderMenu() throws IOException { + + String menuDefFile = (String)templateCtx.get("menuDefFile"); + String menuName = (String)templateCtx.get("menuName"); + String menuWrapperClassName = (String)templateCtx.get("menuWrapperClassName"); + HtmlMenuWrapper menuWrapper = HtmlMenuWrapper.getMenuWrapper(request, response, session, menuDefFile, menuName, menuWrapperClassName); + + if (menuWrapper == null) { + throw new IOException("HtmlMenuWrapper with def file:" + menuDefFile + " menuName:" + menuName + " and HtmlMenuWrapper class:" + menuWrapperClassName + " could not be instantiated."); + } + + String associatedContentId = (String)templateCtx.get("associatedContentId"); + menuWrapper.putInContext("defaultAssociatedContentId", associatedContentId); + menuWrapper.putInContext("currentValue", view); + + String menuStr = menuWrapper.renderMenuString(); + out.write(menuStr); + } + + }; + } +} |
Free forum by Nabble | Edit this page |