    001// Licensed under the Apache License, Version 2.0 (the "License");
    002// you may not use this file except in compliance with the License.
    003// You may obtain a copy of the License at
    004//
    005//     http://www.apache.org/licenses/LICENSE-2.0
    006//
    007// Unless required by applicable law or agreed to in writing, software
    008// distributed under the License is distributed on an "AS IS" BASIS,
    009// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    010// See the License for the specific language governing permissions and
    011// limitations under the License.
    012
    013package org.apache.tapestry5;
    014
    015import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
    016import org.apache.tapestry5.ioc.internal.util.InternalUtils;
    017
    018import java.io.Serializable;
    019import java.util.Collections;
    020import java.util.List;
    021import java.util.Map;
    022
    023/**
    024 * Standard implementation of {@link ValidationTracker}. Works pretty hard to ensure a minimum
    025 * amount of data is stored
    026 * in the HttpSession.
    027 */
    028public final class ValidationTrackerImpl extends BaseOptimizedSessionPersistedObject implements ValidationTracker, Serializable
    029{
    030    private static final long serialVersionUID = -8029192726659275677L;
    031
    032    private static class FieldTracker implements Serializable
    033    {
    034        private static final long serialVersionUID = -3653306147088451811L;
    035
    036        private final String validationId;
    037
    038        private String input;
    039
    040        private String errorMessage;
    041
    042        FieldTracker(String validationId)
    043        {
    044            this.validationId = validationId;
    045        }
    046    }
    047
    048    private List<String> extraErrors;
    049
    050    private List<FieldTracker> fieldTrackers;
    051
    052    // Rebuilt on-demand
    053    // Keyed on validationId
    054
    055    private transient Map<String, FieldTracker> fieldToTracker;
    056
    057    private void refreshFieldToTracker()
    058    {
    059        if (fieldToTracker != null)
    060            return;
    061
    062        if (fieldTrackers == null)
    063            return;
    064
    065        fieldToTracker = CollectionFactory.newMap();
    066
    067        for (FieldTracker ft : fieldTrackers)
    068            fieldToTracker.put(ft.validationId, ft);
    069    }
    070
    071    private String getKey(Field field)
    072    {
    073        if (field instanceof Field2)
    074        {
    075            Field2 field2 = (Field2) field;
    076            return field2.getValidationId();
    077        }
    078
    079        return field.getControlName();
    080    }
    081
    082    private FieldTracker get(Field field)
    083    {
    084        String key = getKey(field);
    085
    086        refreshFieldToTracker();
    087
    088        FieldTracker result = InternalUtils.get(fieldToTracker, key);
    089
    090        if (result == null)
    091            result = new FieldTracker(key);
    092
    093        return result;
    094    }
    095
    096    private void store(FieldTracker fieldTracker)
    097    {
    098        if (fieldTrackers == null)
    099            fieldTrackers = CollectionFactory.newList();
    100
    101        refreshFieldToTracker();
    102
    103        String key = fieldTracker.validationId;
    104
    105        if (!fieldToTracker.containsKey(key))
    106        {
    107            fieldTrackers.add(fieldTracker);
    108            fieldToTracker.put(key, fieldTracker);
    109        }
    110
    111        markDirty();
    112    }
    113
    114    public void clear()
    115    {
    116        extraErrors = null;
    117        fieldTrackers = null;
    118        fieldToTracker = null;
    119
    120        markDirty();
    121    }
    122
    123    public String getError(Field field)
    124    {
    125        return get(field).errorMessage;
    126    }
    127
    128    public List<String> getErrors()
    129    {
    130        List<String> result = CollectionFactory.newList();
    131
    132        if (extraErrors != null)
    133            result.addAll(extraErrors);
    134
    135        if (fieldTrackers != null)
    136        {
    137            for (FieldTracker ft : fieldTrackers)
    138            {
    139                String errorMessage = ft.errorMessage;
    140
    141                if (errorMessage != null)
    142                    result.add(errorMessage);
    143            }
    144        }
    145
    146        return result;
    147    }
    148
    149    public List<String> getUnassociatedErrors()
    150    {
    151        if (extraErrors == null)
    152        {
    153            return Collections.emptyList();
    154        }
    155
    156        return Collections.unmodifiableList(extraErrors);
    157    }
    158
    159    public boolean getHasErrors()
    160    {
    161        return !getErrors().isEmpty();
    162    }
    163
    164    public String getInput(Field field)
    165    {
    166        return get(field).input;
    167    }
    168
    169    public boolean inError(Field field)
    170    {
    171        return InternalUtils.isNonBlank(get(field).errorMessage);
    172    }
    173
    174    public void recordError(Field field, String errorMessage)
    175    {
    176        FieldTracker ft = get(field);
    177
    178        ft.errorMessage = errorMessage;
    179
    180        store(ft);
    181    }
    182
    183    public void recordError(String errorMessage)
    184    {
    185        if (extraErrors == null)
    186            extraErrors = CollectionFactory.newList();
    187
    188        extraErrors.add(errorMessage);
    189
    190        markDirty();
    191    }
    192
    193    public void recordInput(Field field, String input)
    194    {
    195        FieldTracker ft = get(field);
    196
    197        ft.input = input;
    198
    199        store(ft);
    200    }
    201}