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}