Skip to content
Permalink
Browse files

Improved: Make ‘FormRenderer#getUsedFields’ more generic

(OFBIZ-11135)

We want to be able to reuse the selection of used fields for any form
renderers. As a consequence it seems more appropriate to move the
behavior of ‘FormRenderer#getUsedFields’ to the ‘ModelFormField’
class.

Additionally we have decoupled the filtering logic from the iteration
process by constructing a stateful predicate.

The corresponding unit tests have been adapted to both changes.


git-svn-id: https://svn.apache.org/repos/asf/ofbiz/ofbiz-framework/trunk@1862636 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information
mthl committed Jul 5, 2019
1 parent d9a0273 commit e10cf90e092761f6a37f62a2d7b5829499fa0f0f
@@ -35,6 +35,8 @@
import java.util.Map;
import java.util.StringTokenizer;
import java.util.TimeZone;
import java.util.function.Consumer;
import java.util.function.Predicate;

import org.apache.ofbiz.base.conversion.ConversionException;
import org.apache.ofbiz.base.conversion.DateTimeConverters;
@@ -102,6 +104,18 @@

public static final String module = ModelFormField.class.getName();

/**
* Constructs a form field model from a builder specification.
*
* @param spec the specification of form field definition
* @return the form field model corresponding to the specification.
*/
public static ModelFormField from(Consumer<ModelFormFieldBuilder> spec) {
ModelFormFieldBuilder builder = new ModelFormFieldBuilder();
spec.accept(builder);
return new ModelFormField(builder);
}

public static ModelFormField from(ModelFormFieldBuilder builder) {
return new ModelFormField(builder);
}
@@ -929,6 +943,27 @@ public boolean shouldUse(Map<String, Object> context) {
}
}

/**
* Provides a stateful predicate checking if a field must be used.
*
* @param context the context determining if the field must be used
* @return a stateful predicate checking if a field must be used.
*/
public static Predicate<ModelFormField> usedFields(Map<String, Object> context) {
HashMap<String, Boolean> seenUseWhen = new HashMap<>();
return field -> {
if (!field.isUseWhenEmpty()) {
String name = field.getName();
boolean shouldUse = field.shouldUse(context);
if (seenUseWhen.containsKey(name)) {
return shouldUse != seenUseWhen.get(name);
}
seenUseWhen.put(name, shouldUse);
}
return true;
};
}

/**
* Models the &lt;auto-complete&gt; element.
*
@@ -4360,4 +4395,4 @@ public void renderFieldString(Appendable writer, Map<String, Object> context, Fo
formStringRenderer.renderTextFindField(writer, context, this);
}
}
}
}
@@ -18,6 +18,8 @@
*******************************************************************************/
package org.apache.ofbiz.widget.renderer;

import static org.apache.ofbiz.widget.model.ModelFormField.usedFields;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
@@ -668,25 +670,6 @@ private void renderItemRow(Appendable writer, Map<String, Object> localContext,
formStringRenderer.renderFormatItemRowClose(writer, localContext, modelForm);
}

// Filter the field lists by removing the ones with both the same names and "use-when" values.
// Keep all fields without a "use-when" attribute.
List<ModelFormField> getUsedFields(Map<String, Object> context) {
HashMap<String, Boolean> seenUseWhen = new HashMap<>();
return modelForm.getFieldList().stream()
.filter(field -> {
if (!field.isUseWhenEmpty()) {
String name = field.getName();
boolean shouldUse = field.shouldUse(context);
if (seenUseWhen.containsKey(name)) {
return shouldUse != seenUseWhen.get(name);
}
seenUseWhen.put(name, shouldUse);
}
return true;
})
.collect(Collectors.toList());
}

private void renderItemRows(Appendable writer, Map<String, Object> context, FormStringRenderer formStringRenderer,
boolean formPerItem, int numOfColumns) throws IOException {
String lookupName = modelForm.getListName();
@@ -777,7 +760,9 @@ private void renderItemRows(Appendable writer, Map<String, Object> context, Form
Debug.logVerbose("In form got another row, context is: " + localContext, module);
}

List<ModelFormField> tempFieldList = getUsedFields(localContext);
List<ModelFormField> tempFieldList = modelForm.getFieldList().stream()
.filter(usedFields(localContext))
.collect(Collectors.toList());

// Each single item is rendered in one or more rows if its fields have
// different "position" attributes. All the fields with the same position
@@ -972,7 +957,9 @@ private void renderMultiFormString(Appendable writer, Map<String, Object> contex

private void renderSingleFormString(Appendable writer, Map<String, Object> context,
int positions) throws IOException {
List<ModelFormField> tempFieldList = getUsedFields(context);
List<ModelFormField> tempFieldList = modelForm.getFieldList().stream()
.filter(usedFields(context))
.collect(Collectors.toList());
Set<String> alreadyRendered = new TreeSet<>();
FieldGroup lastFieldGroup = null;
// render form open
@@ -0,0 +1,84 @@
/*
* 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.apache.ofbiz.widget.model;

import static org.apache.ofbiz.widget.model.ModelFormField.from;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.Assert.assertThat;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

import org.junit.Before;
import org.junit.Test;

public class ModelFormFieldTest {
private HashMap<String, Object> context;

@Before
public void setUp() throws Exception {
context = new HashMap<>();
}

/**
* Filter a list of fields with {@link ModelFormField#usedFields} predicate.
* <p>
* This is useful since Hamcrest does not provide any Stream matchers.
*
* @param fields the fields to filter
* @return a list a filtered fields.
*/
List<ModelFormField> getUsedField(ModelFormField... fields) {
return Arrays.stream(fields)
.filter(ModelFormField.usedFields(context))
.collect(Collectors.toList());
}

@Test
public void fieldsToRenderBasic() {
ModelFormField fA = from(b -> b.setName("A"));
ModelFormField fB = from(b -> b.setName("B"));
assertThat(getUsedField(fA, fB), containsInAnyOrder(fA, fB));
}

@Test
public void fieldsToRenderDuplicates() {
ModelFormField fA0 = from(b -> b.setName("A"));
ModelFormField fB = from(b -> b.setName("B"));
ModelFormField fA1 = from(b -> b.setName("A"));
assertThat(getUsedField(fA0, fB, fA1), containsInAnyOrder(fA0, fA1, fB));
}

@Test
public void fieldsToRenderBasicUseWhen() {
ModelFormField fA0 = from(b -> b.setName("A").setUseWhen("true"));
ModelFormField fA1 = from(b -> b.setName("A").setUseWhen("false"));
assertThat(getUsedField(fA0, fA1), containsInAnyOrder(fA0, fA1));
}

@Test
public void fieldsToRenderDuplicatesUseWhen() {
ModelFormField fA0 = from(b -> b.setName("A").setUseWhen("true"));
ModelFormField fA1 = from(b -> b.setName("A").setUseWhen("false"));
ModelFormField fA2 = from(b -> b.setName("A").setUseWhen("true"));
assertThat(getUsedField(fA0, fA1, fA2), containsInAnyOrder(fA0, fA1));
}
}

This file was deleted.

0 comments on commit e10cf90

Please sign in to comment.
You can’t perform that action at this time.