com.astamuse.asta4d.web.form.field.impl.AbstractRadioAndCheckboxRenderer.java Source code

Java tutorial

Introduction

Here is the source code for com.astamuse.asta4d.web.form.field.impl.AbstractRadioAndCheckboxRenderer.java

Source

/*
 * Copyright 2014 astamuse company,Ltd.
 * 
 * Licensed 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 com.astamuse.asta4d.web.form.field.impl;

import static com.astamuse.asta4d.render.SpecialRenderer.Clear;

import java.util.LinkedList;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.jsoup.nodes.Element;

import com.astamuse.asta4d.Configuration;
import com.astamuse.asta4d.extnode.GroupNode;
import com.astamuse.asta4d.render.ElementNotFoundHandler;
import com.astamuse.asta4d.render.ElementSetter;
import com.astamuse.asta4d.render.Renderable;
import com.astamuse.asta4d.render.Renderer;
import com.astamuse.asta4d.render.transformer.ElementTransformer;
import com.astamuse.asta4d.util.SelectorUtil;
import com.astamuse.asta4d.web.form.field.OptionValueMap;
import com.astamuse.asta4d.web.form.field.OptionValuePair;
import com.astamuse.asta4d.web.form.field.PrepareRenderingDataUtil;
import com.astamuse.asta4d.web.form.field.SimpleFormFieldWithOptionValueRenderer;
import com.astamuse.asta4d.web.util.ClosureVarRef;

public class AbstractRadioAndCheckboxRenderer extends SimpleFormFieldWithOptionValueRenderer {

    private static final String ToBeHiddenLaterFlagAttr = Configuration.getConfiguration().getTagNameSpace() + ":"
            + "ToBeHiddenLaterFlagAttr";

    @Override
    public Renderer renderForEdit(String editTargetSelector, Object value) {
        final List<String> valueList = convertValueToList(value);
        Renderer renderer = Renderer.create("input", "checked", Clear);
        // we have to iterate the elements because the attr selector would not work for blank values.
        renderer.add("input", new ElementSetter() {
            @Override
            public void set(Element elem) {
                String val = elem.attr("value");
                if (valueList.contains(val)) {
                    elem.attr("checked", "");
                }
            }
        });
        return Renderer.create(editTargetSelector, renderer);
    }

    /**
     * By default, there must be an id for every radio/checkbox item.
     * 
     * @return
     */
    protected boolean allowNonIdItems() {
        return false;
    }

    protected Renderer retrieveAndCreateValueMap(final String editTargetSelector,
            final String displayTargetSelector) {
        Renderer render = Renderer.create();
        if (PrepareRenderingDataUtil.retrieveStoredDataFromContextBySelector(editTargetSelector) == null) {

            final List<Pair<String, String>> inputList = new LinkedList<>();

            final List<OptionValuePair> optionList = new LinkedList<>();

            render.add(editTargetSelector, new ElementSetter() {
                @Override
                public void set(Element elem) {
                    inputList.add(Pair.of(elem.id(), elem.attr("value")));
                }
            });

            render.add(":root", new Renderable() {
                @Override
                public Renderer render() {
                    Renderer render = Renderer.create();
                    for (Pair<String, String> input : inputList) {
                        String id = input.getLeft();
                        final String value = input.getRight();
                        if (StringUtils.isEmpty(id)) {
                            if (allowNonIdItems()) {
                                optionList.add(new OptionValuePair(value, value));
                            } else {
                                String msg = "The target item[%s] must have id specified.";
                                throw new IllegalArgumentException(String.format(msg, editTargetSelector));
                            }
                        } else {
                            render.add(SelectorUtil.attr("for", id), Renderer.create("label", new ElementSetter() {
                                @Override
                                public void set(Element elem) {
                                    optionList.add(new OptionValuePair(value, elem.text()));
                                }
                            }));
                            render.add(":root", new Renderable() {
                                @Override
                                public Renderer render() {
                                    PrepareRenderingDataUtil.storeDataToContextBySelector(editTargetSelector,
                                            displayTargetSelector, new OptionValueMap(optionList));
                                    return Renderer.create();
                                }
                            });
                        }
                    } // end for loop
                    return render;
                }
            });
        }
        return render;
    }

    protected Renderer setDelayedHiddenFlag(final String targetSelector) {
        // hide the input element
        final List<String> duplicatorRefList = new LinkedList<>();
        final List<String> idList = new LinkedList<>();
        Renderer renderer = Renderer.create(targetSelector, new ElementSetter() {
            @Override
            public void set(Element elem) {
                String duplicatorRef = elem.attr(RadioPrepareRenderer.DUPLICATOR_REF_ATTR);
                if (StringUtils.isNotEmpty(duplicatorRef)) {
                    duplicatorRefList.add(duplicatorRef);
                }
                idList.add(elem.id());
            }
        });
        return renderer.add(":root", new Renderable() {
            @Override
            public Renderer render() {
                Renderer render = Renderer.create().disableMissingSelectorWarning();

                for (String ref : duplicatorRefList) {
                    render.add(SelectorUtil.attr(RadioPrepareRenderer.DUPLICATOR_REF_ID_ATTR, ref),
                            ToBeHiddenLaterFlagAttr, "");
                }
                for (String id : idList) {
                    render.add(SelectorUtil.attr(RadioPrepareRenderer.LABEL_REF_ATTR, id), ToBeHiddenLaterFlagAttr,
                            "");
                }
                for (String id : idList) {
                    render.add(SelectorUtil.attr("label", "for", id), ToBeHiddenLaterFlagAttr, "");
                }
                render.add(targetSelector, ToBeHiddenLaterFlagAttr, "");
                // render.addDebugger("after set hidden flag");
                return render.enableMissingSelectorWarning();
            }
        });
    }

    @Override
    public Renderer renderForDisplay(final String editTargetSelector, final String displayTargetSelector,
            final Object value) {

        Renderer render = Renderer.create().disableMissingSelectorWarning();

        // retrieve and create a value map here
        render.add(retrieveAndCreateValueMap(editTargetSelector, displayTargetSelector));

        // render.add(super.renderForDisplay(editTargetSelector, displayTargetSelector, nonNullString));

        // hide the edit element
        render.add(setDelayedHiddenFlag(editTargetSelector));

        final List<String> valueList = convertValueToList(value);

        // render the shown value to target element by displayTargetSelector
        render.add(displayTargetSelector, new Renderable() {
            @Override
            public Renderer render() {
                return Renderer.create(displayTargetSelector, valueList, v -> {
                    return renderToDisplayTarget(displayTargetSelector,
                            retrieveDisplayStringFromStoredOptionValueMap(displayTargetSelector, v));
                });
            }
        });

        // if the element by displayTargetSelector does not exists, simply add a span to show the value.
        // since ElementNotFoundHandler has been delayed, so the Renderable is not necessary
        render.add(new ElementNotFoundHandler(displayTargetSelector) {
            @Override
            public Renderer alternativeRenderer() {
                return addAlternativeDom(editTargetSelector, valueList);
            }
        });

        // delay to hide all
        render.add(":root", new Renderable() {
            @Override
            public Renderer render() {
                return hideTarget(SelectorUtil.attr(ToBeHiddenLaterFlagAttr));
            }
        });

        // delay to remove the redundant attr
        render.add(":root", new Renderable() {
            @Override
            public Renderer render() {
                Renderer render = Renderer.create().disableMissingSelectorWarning();
                return render.add(SelectorUtil.attr(ToBeHiddenLaterFlagAttr), ToBeHiddenLaterFlagAttr, Clear)
                        .enableMissingSelectorWarning();
            }
        });
        return render.enableMissingSelectorWarning();
    }

    protected Renderer addAlternativeDom(final String editTargetSelector, final List<String> valueList) {
        Renderer renderer = Renderer.create();

        // renderer.addDebugger("entry root");

        // renderer.addDebugger("entry root:edit target:", editTargetSelector);

        final List<String> matchedIdList = new LinkedList<>();
        final List<String> unMatchedIdList = new LinkedList<>();

        renderer.add(editTargetSelector, new ElementSetter() {
            @Override
            public void set(Element elem) {
                if (valueList.contains((elem.attr("value")))) {
                    matchedIdList.add(elem.id());
                } else {
                    unMatchedIdList.add(elem.id());
                }
            }
        });

        renderer.add(":root", new Renderable() {
            @Override
            public Renderer render() {
                Renderer renderer = Renderer.create().disableMissingSelectorWarning();

                // renderer.addDebugger("before hide unmatch");
                // renderer.addDebugger("before add match");

                if (matchedIdList.isEmpty()) {
                    renderer.add(addDefaultAlternativeDom(editTargetSelector, valueList));
                } else {
                    // do nothing for remaining the existing label element
                    // but we still have to revive the possibly existing duplicate container
                    for (final String inputId : matchedIdList) {
                        final List<String> matchedDuplicatorRefList = new LinkedList<>();
                        final String labelRefSelector = SelectorUtil.attr(RadioPrepareRenderer.LABEL_REF_ATTR,
                                inputId);
                        final String labelDefaultSelector = SelectorUtil.attr(SelectorUtil.tag("label"), "for",
                                inputId);
                        renderer.add(labelRefSelector, new ElementSetter() {
                            @Override
                            public void set(Element elem) {
                                String ref = elem.attr(RadioPrepareRenderer.DUPLICATOR_REF_ATTR);
                                if (StringUtils.isNotEmpty(ref)) {
                                    matchedDuplicatorRefList.add(ref);
                                }
                            }
                        });
                        renderer.add(new ElementNotFoundHandler(labelRefSelector) {
                            @Override
                            public Renderer alternativeRenderer() {
                                return Renderer.create(labelDefaultSelector, new ElementSetter() {
                                    @Override
                                    public void set(Element elem) {
                                        String ref = elem.attr(RadioPrepareRenderer.DUPLICATOR_REF_ATTR);
                                        if (StringUtils.isNotEmpty(ref)) {
                                            matchedDuplicatorRefList.add(ref);
                                        }
                                    }// end set
                                });
                            }// end alternativeRenderer
                        });// end ElementNotFoundHandler
                        renderer.add(":root", new Renderable() {
                            @Override
                            public Renderer render() {
                                Renderer renderer = Renderer.create().disableMissingSelectorWarning();
                                for (String ref : matchedDuplicatorRefList) {
                                    renderer.add(
                                            SelectorUtil.attr(RadioPrepareRenderer.DUPLICATOR_REF_ID_ATTR, ref),
                                            ToBeHiddenLaterFlagAttr, Clear);
                                }
                                renderer.add(labelRefSelector, ToBeHiddenLaterFlagAttr, Clear);
                                renderer.add(labelDefaultSelector, ToBeHiddenLaterFlagAttr, Clear);
                                return renderer.enableMissingSelectorWarning();
                            }
                        });
                    }
                }
                return renderer.enableMissingSelectorWarning();
            }
        });

        return renderer;

    }

    protected Renderer addDefaultAlternativeDom(final String editTargetSelector, final List<String> valueList) {
        final List<String> duplicatorRefList = new LinkedList<>();
        final List<String> idList = new LinkedList<>();
        ClosureVarRef<Boolean> editTargetExists = new ClosureVarRef<Boolean>(false);
        Renderer renderer = Renderer.create(editTargetSelector, new ElementSetter() {
            @Override
            public void set(Element elem) {
                String duplicatorRef = elem.attr(RadioPrepareRenderer.DUPLICATOR_REF_ATTR);
                if (StringUtils.isNotEmpty(duplicatorRef)) {
                    duplicatorRefList.add(duplicatorRef);
                }
                idList.add(elem.id());
                editTargetExists.set(true);
            }
        });

        /*
        renderer.add(":root", () -> {
        return Renderer.create().addDebugger("current root for addDefaultAlternativeDom");
        });
        */

        renderer.add(":root", new Renderable() {
            @Override
            public Renderer render() {
                // skip create display alternative DOM if edit target does not exist.
                if (editTargetExists.get()) {
                    // it is OK
                } else {
                    return Renderer.create();
                }

                String attachTargetSelector;
                if (duplicatorRefList.size() > 0) {
                    attachTargetSelector = SelectorUtil.attr(RadioPrepareRenderer.DUPLICATOR_REF_ID_ATTR,
                            duplicatorRefList.get(duplicatorRefList.size() - 1));
                } else if (idList.size() == 0) {
                    String msg = "The target item[%s] must have id specified.";
                    throw new IllegalArgumentException(String.format(msg, editTargetSelector));
                } else {
                    attachTargetSelector = SelectorUtil.id(idList.get(idList.size() - 1));
                }
                return new Renderer(attachTargetSelector, new ElementTransformer(null) {
                    @Override
                    public Element invoke(Element elem) {
                        GroupNode group = new GroupNode();

                        Element editClone = elem.clone();
                        group.appendChild(editClone);

                        for (String v : valueList) {
                            String nonNullString = retrieveDisplayStringFromStoredOptionValueMap(editTargetSelector,
                                    v);
                            group.appendChild(createAlternativeDisplayElement(nonNullString));
                        }
                        return group;
                    }// invoke
                });// new renderer
            }// render()
        });// renderable

        return renderer;
    }

}