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