    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.commons.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        return field.getControlName();
    074    }
    075
    076    private FieldTracker get(Field field)
    077    {
    078        String key = getKey(field);
    079
    080        refreshFieldToTracker();
    081
    082        FieldTracker result = InternalUtils.get(fieldToTracker, key);
    083
    084        if (result == null)
    085            result = new FieldTracker(key);
    086
    087        return result;
    088    }
    089
    090    private void store(FieldTracker fieldTracker)
    091    {
    092        if (fieldTrackers == null)
    093            fieldTrackers = CollectionFactory.newList();
    094
    095        refreshFieldToTracker();
    096
    097        String key = fieldTracker.validationId;
    098
    099        if (!fieldToTracker.containsKey(key))
    100        {
    101            fieldTrackers.add(fieldTracker);
    102            fieldToTracker.put(key, fieldTracker);
    103        }
    104
    105        markDirty();
    106    }
    107
    108    public void clear()
    109    {
    110        extraErrors = null;
    111        fieldTrackers = null;
    112        fieldToTracker = null;
    113
    114        markDirty();
    115    }
    116
    117    public String getError(Field field)
    118    {
    119        return get(field).errorMessage;
    120    }
    121
    122    public List<String> getErrors()
    123    {
    124        List<String> result = CollectionFactory.newList();
    125
    126        if (extraErrors != null)
    127            result.addAll(extraErrors);
    128
    129        if (fieldTrackers != null)
    130        {
    131            for (FieldTracker ft : fieldTrackers)
    132            {
    133                String errorMessage = ft.errorMessage;
    134
    135                if (errorMessage != null)
    136                    result.add(errorMessage);
    137            }
    138        }
    139
    140        return result;
    141    }
    142
    143    public List<String> getUnassociatedErrors()
    144    {
    145        if (extraErrors == null)
    146        {
    147            return Collections.emptyList();
    148        }
    149
    150        return Collections.unmodifiableList(extraErrors);
    151    }
    152
    153    public boolean getHasErrors()
    154    {
    155        return !getErrors().isEmpty();
    156    }
    157
    158    public String getInput(Field field)
    159    {
    160        return get(field).input;
    161    }
    162
    163    public boolean inError(Field field)
    164    {
    165        return InternalUtils.isNonBlank(get(field).errorMessage);
    166    }
    167
    168    public void recordError(Field field, String errorMessage)
    169    {
    170        FieldTracker ft = get(field);
    171
    172        ft.errorMessage = errorMessage;
    173
    174        store(ft);
    175    }
    176
    177    public void recordError(String errorMessage)
    178    {
    179        if (extraErrors == null)
    180            extraErrors = CollectionFactory.newList();
    181
    182        extraErrors.add(errorMessage);
    183
    184        markDirty();
    185    }
    186
    187    public void recordInput(Field field, String input)
    188    {
    189        FieldTracker ft = get(field);
    190
    191        ft.input = input;
    192
    193        store(ft);
    194    }
    195}