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}