001    // Copyright 2006, 2008, 2010, 2011 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    
015    package org.apache.tapestry5;
016    
017    import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
018    import org.apache.tapestry5.ioc.internal.util.InternalUtils;
019    
020    import java.io.Serializable;
021    import java.util.List;
022    import java.util.Map;
023    
024    /**
025     * Standard implementation of {@link ValidationTracker}. Works pretty hard to ensure a minimum
026     * amount of data is stored
027     * in the HttpSession.
028     */
029    public final class ValidationTrackerImpl extends BaseOptimizedSessionPersistedObject implements ValidationTracker, Serializable
030    {
031        private static final long serialVersionUID = -8029192726659275677L;
032    
033        private static class FieldTracker implements Serializable
034        {
035            private static final long serialVersionUID = -3653306147088451811L;
036    
037            private final String fieldName;
038    
039            private String input;
040    
041            private String errorMessage;
042    
043            FieldTracker(String fieldName)
044            {
045                this.fieldName = fieldName;
046            }
047        }
048    
049        private List<String> extraErrors;
050    
051        private List<FieldTracker> fieldTrackers;
052    
053        // Rebuilt on-demand
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.fieldName, ft);
069        }
070    
071        private FieldTracker get(Field field)
072        {
073            String key = field.getControlName();
074    
075            refreshFieldToTracker();
076    
077            FieldTracker result = InternalUtils.get(fieldToTracker, key);
078    
079            if (result == null)
080                result = new FieldTracker(key);
081    
082            return result;
083        }
084    
085        private void store(FieldTracker fieldTracker)
086        {
087            if (fieldTrackers == null)
088                fieldTrackers = CollectionFactory.newList();
089    
090            refreshFieldToTracker();
091    
092            String key = fieldTracker.fieldName;
093    
094            if (!fieldToTracker.containsKey(key))
095            {
096                fieldTrackers.add(fieldTracker);
097                fieldToTracker.put(key, fieldTracker);
098            }
099    
100            markDirty();
101        }
102    
103        public void clear()
104        {
105            extraErrors = null;
106            fieldTrackers = null;
107            fieldToTracker = null;
108    
109            markDirty();
110        }
111    
112        public String getError(Field field)
113        {
114            return get(field).errorMessage;
115        }
116    
117        public List<String> getErrors()
118        {
119            List<String> result = CollectionFactory.newList();
120    
121            if (extraErrors != null)
122                result.addAll(extraErrors);
123    
124            if (fieldTrackers != null)
125            {
126                for (FieldTracker ft : fieldTrackers)
127                {
128                    String errorMessage = ft.errorMessage;
129    
130                    if (errorMessage != null)
131                        result.add(errorMessage);
132                }
133            }
134    
135            return result;
136        }
137    
138        public boolean getHasErrors()
139        {
140            return !getErrors().isEmpty();
141        }
142    
143        public String getInput(Field field)
144        {
145            return get(field).input;
146        }
147    
148        public boolean inError(Field field)
149        {
150            return InternalUtils.isNonBlank(get(field).errorMessage);
151        }
152    
153        public void recordError(Field field, String errorMessage)
154        {
155            FieldTracker ft = get(field);
156    
157            ft.errorMessage = errorMessage;
158    
159            store(ft);
160        }
161    
162        public void recordError(String errorMessage)
163        {
164            if (extraErrors == null)
165                extraErrors = CollectionFactory.newList();
166    
167            extraErrors.add(errorMessage);
168    
169            markDirty();
170        }
171    
172        public void recordInput(Field field, String input)
173        {
174            FieldTracker ft = get(field);
175    
176            ft.input = input;
177    
178            store(ft);
179        }
180    }