001    package org.apache.fulcrum.intake.validator;
002    
003    /*
004     * Licensed to the Apache Software Foundation (ASF) under one
005     * or more contributor license agreements.  See the NOTICE file
006     * distributed with this work for additional information
007     * regarding copyright ownership.  The ASF licenses this file
008     * to you under the Apache License, Version 2.0 (the
009     * "License"); you may not use this file except in compliance
010     * with the License.  You may obtain a copy of the License at
011     *
012     *   http://www.apache.org/licenses/LICENSE-2.0
013     *
014     * Unless required by applicable law or agreed to in writing,
015     * software distributed under the License is distributed on an
016     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017     * KIND, either express or implied.  See the License for the
018     * specific language governing permissions and limitations
019     * under the License.
020     */
021    
022    import java.util.Iterator;
023    import java.util.List;
024    
025    import org.apache.commons.logging.Log;
026    import org.apache.commons.logging.LogFactory;
027    import org.apache.fulcrum.intake.IntakeException;
028    import org.apache.fulcrum.intake.model.Field;
029    import org.apache.fulcrum.intake.model.Group;
030    
031    /**
032     * Helper Class to manage relations between fields. The following
033     * comparisons are supported:
034     * 
035     * <table>
036     * <tr>
037     *   <th>Name</th><th>Valid Values</th><th>Default Value</th>
038     * </tr>
039     * <tr>
040     *   <td>less-than</td>
041     *   <td>&lt;name of other field&gt;</td>
042     *   <td>&nbsp;</td>
043     * </tr>
044     * <tr>
045     *   <td>greater-than</td>
046     *   <td>&lt;name of other field&gt;</td>
047     *   <td>&nbsp;</td>
048     * </tr>
049     * <tr>
050     *   <td>less-than-or-equal</td>
051     *   <td>&lt;name of other field&gt;</td>
052     *   <td>&nbsp;</td>
053     * </tr>
054     * <tr>
055     *   <td>greater-than-or-equal</td>
056     *   <td>&lt;name of other field&gt;</td>
057     *   <td>&nbsp;</td>
058     * </tr>
059     * </table>
060     *
061     * @author <a href="mailto:tv@apache.org">Thomas Vandahl</a>
062     * @version $Id: DateStringValidator.java 534527 2007-05-02 16:10:59Z tv $
063     */
064    public class FieldReference
065    {
066        /** a local logger */
067        protected static final Log log = LogFactory.getLog(FieldReference.class);
068        
069        /** Rule name for "&lt;" comparison */
070        public static final String RANGE_LT = "less-than";
071    
072        /** Rule name for "&gt;" comparison */
073        public static final String RANGE_GT = "greater-than";
074    
075        /** Rule name for "&lt;=" comparison */
076        public static final String RANGE_LTE = "less-than-or-equal";
077    
078        /** Rule name for "&gt;=" comparison */
079        public static final String RANGE_GTE = "greater-than-or-equal";
080    
081        /** Integer value for "&lt;" comparison */
082        public static final int COMPARE_LT = 1;
083    
084        /** Integer value for "&gt;" comparison */
085        public static final int COMPARE_GT = 2;
086    
087        /** Integer value for "&lt;=" comparison */
088        public static final int COMPARE_LTE = 3;
089    
090        /** Integer value for "&gt;=" comparison */
091        public static final int COMPARE_GTE = 4;
092    
093        /** Numeric comparison */
094        private int compare = 0;
095        
096        /** Name of referenced field */
097        private String fieldName = null;
098    
099        /** Error message */
100        private String message = null;
101        
102        /**
103         *  Constructor
104         */
105        public FieldReference()
106        {
107            // do nothing
108        }
109    
110        /**
111         * @return the comparison type
112         */
113        public int getCompare()
114        {
115            return compare;
116        }
117    
118        /**
119         * @param compare the comparison type to set
120         */
121        public void setCompare(int compare)
122        {
123            this.compare = compare;
124        }
125    
126        /**
127         * @return the field name
128         */
129        public String getFieldName()
130        {
131            return fieldName;
132        }
133    
134        /**
135         * @param fieldName the field name to set
136         */
137        public void setFieldName(String fieldName)
138        {
139            this.fieldName = fieldName;
140        }
141    
142        /**
143         * @return the message
144         */
145        public String getMessage()
146        {
147            return message;
148        }
149    
150        /**
151         * @param message the message to set
152         */
153        public void setMessage(String message)
154        {
155            this.message = message;
156        }
157        
158        /**
159         * Map the comparison strings to their numeric counterparts
160         * 
161         * @param key the string representation of a comparison operator
162         * @return the numeric representation of the given comparison operator
163         */
164        public static int getCompareType(String key)
165        {
166            int compareType = 0;
167            
168            if (key.equals(RANGE_LT))
169            {
170                compareType = COMPARE_LT;
171            }
172            else if (key.equals(RANGE_LTE))
173            {
174                compareType = COMPARE_LTE;
175            }
176            else if (key.equals(RANGE_GT))
177            {
178                compareType = COMPARE_GT;
179            }
180            else if (key.equals(RANGE_GTE))
181            {
182                compareType = COMPARE_GTE;
183            }
184            
185            return compareType;
186        }
187        
188        /**
189         * Check the parsed value against the referenced fields
190         * 
191         * @param fieldReferences List of field references to check
192         * @param compareCallback Callback to the actual compare operation
193         * @param value the parsed value of the related field
194         * @param group the group the related field belongs to
195         * 
196         * @throws ValidationException
197         */
198        public static void checkReferences(List fieldReferences, CompareCallback compareCallback, 
199                Object value, Group group)
200            throws ValidationException
201        {
202            for (Iterator i = fieldReferences.iterator(); i.hasNext();)
203            {
204                FieldReference ref = (FieldReference)i.next();
205                boolean comp_true = true;
206    
207                try
208                {
209                    Field refField = group.get(ref.getFieldName());
210                    
211                    if (refField.isSet())
212                    {
213                        /*
214                         * Fields are processed in sequence so that our
215                         * reference field might have been set but not
216                         * yet validated. We check this here.
217                         */
218                        if (!refField.isValidated())
219                        {
220                            refField.validate();
221                        }
222                        
223                        if (refField.isValid())
224                        {
225                            try
226                            {
227                                comp_true = compareCallback.compareValues(ref.getCompare(), 
228                                        value, 
229                                        refField.getValue());
230                            }
231                            catch (ClassCastException e)
232                            {
233                                throw new IntakeException("Type mismatch comparing " +
234                                        value + " with " + refField.getValue(), e);
235                            }
236                        }
237                    }
238                }
239                catch (IntakeException e)
240                {
241                    log.error("Validate operation failed.", e);
242                    throw new ValidationException(ref.getMessage());
243                }
244    
245                if (comp_true == false)
246                {
247                    throw new ValidationException(ref.getMessage());
248                }
249            }
250        }
251    }