001/*
002 *  Copyright 2012 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.runtime.parameter;
017
018import java.util.HashMap;
019import java.util.Map;
020import java.util.regex.Pattern;
021
022import org.apache.avalon.framework.configuration.Configurable;
023import org.apache.avalon.framework.configuration.Configuration;
024import org.apache.avalon.framework.configuration.ConfigurationException;
025import org.apache.avalon.framework.logger.AbstractLogEnabled;
026import org.apache.avalon.framework.service.ServiceException;
027import org.apache.avalon.framework.service.ServiceManager;
028import org.apache.avalon.framework.service.Serviceable;
029import org.apache.cocoon.util.log.SLF4JLoggerAdapter;
030import org.slf4j.LoggerFactory;
031
032import org.ametys.runtime.i18n.I18nizableText;
033import org.ametys.runtime.plugin.component.PluginAware;
034
035
036/**
037 * This default implementation validates the following configurable stuff:
038 * <ul>
039 *  <li>mandatory: check the parameter is set</li>
040 *  <li>regexp: check the string parameter matches a regexp</li>
041 * </ul> 
042 */
043public class DefaultValidator extends AbstractLogEnabled implements Validator, Configurable, PluginAware, Serviceable
044{
045    /** Is the value mandatory ? */
046    protected boolean _isMandatory;
047    /** Does the value need to match a regexp */
048    protected Pattern _regexp;
049    /** The error text to display if regexp fails */
050    protected I18nizableText _invalidText;
051    /** The plugin name */
052    protected String _pluginName;
053    
054    /** The service manager */
055    protected ServiceManager _smanager;
056    
057    /**
058     * Default constructor for avalon
059     */
060    public DefaultValidator()
061    {
062        // empty
063    }
064    
065    /**
066     * Manual constructor
067     * @param regexp The regexp to check or null
068     * @param mandatory Is the value mandatory 
069     */
070    public DefaultValidator(String regexp, boolean mandatory)
071    {
072        _isMandatory = mandatory;
073        if (regexp != null)
074        {
075            _regexp = Pattern.compile(regexp);
076        }
077        enableLogging(new SLF4JLoggerAdapter(LoggerFactory.getLogger(this.getClass())));
078    }
079    
080    /**
081     * Manual constructor
082     * @param regexp The regexp to check or null
083     * @param invalidText The error text to display
084     * @param mandatory Is the value mandatory 
085     */
086    public DefaultValidator(String regexp, I18nizableText invalidText, boolean mandatory)
087    {
088        _isMandatory = mandatory;
089        if (regexp != null)
090        {
091            _regexp = Pattern.compile(regexp);
092        }
093        _invalidText = invalidText;
094        
095        enableLogging(new SLF4JLoggerAdapter(LoggerFactory.getLogger(this.getClass())));
096    }
097    
098    @Override
099    public void service(ServiceManager smanager) throws ServiceException
100    {
101        _smanager = smanager;
102    }
103    
104    @Override
105    public void setPluginInfo(String pluginName, String featureName, String id)
106    {
107        _pluginName = pluginName;
108    }
109    
110    @Override
111    public void configure(Configuration configuration) throws ConfigurationException
112    {
113        Configuration validatorConfig = configuration.getChild("validation");
114        
115        _isMandatory = validatorConfig.getChild("mandatory", false) != null;
116
117        String regexp = validatorConfig.getChild("regexp").getValue(null);
118        if (regexp != null)
119        {
120            _regexp = Pattern.compile(regexp);
121        }
122        
123        Configuration textConfig = validatorConfig.getChild("invalidText", false);
124        if (textConfig != null)
125        {
126            _invalidText = I18nizableText.parseI18nizableText(textConfig, "plugin." + _pluginName);
127        }
128    }
129    
130    @Override
131    public Map<String, Object> getConfiguration()
132    {
133        Map<String, Object> configuration = new HashMap<>();
134        
135        configuration.put("mandatory", Boolean.valueOf(_isMandatory));
136        
137        if (_regexp != null)
138        {
139            configuration.put("regexp", _regexp);
140        }
141    
142        if (_invalidText != null)
143        {
144            configuration.put("invalidText", _invalidText);
145        }
146        
147        return configuration;
148    }
149    
150    @Override
151    public void validate(Object value, Errors errors)
152    {
153        boolean isArray = value != null && value.getClass().isArray();
154        if (isArray)
155        {
156            validateArrayValues((Object[]) value, errors); 
157        }
158        else
159        {
160            validateSingleValue (value, errors);
161        }
162    }
163    
164    /**
165     * Validates a single value.
166     * @param value the value to validate (can be <code>null</code>).
167     * @param errors the structure to populate if the validation failed.
168     */
169    protected void validateSingleValue (Object value, Errors errors)
170    {
171        if (_isMandatory && (value == null || value.toString().length() == 0))
172        {
173            if (getLogger().isDebugEnabled())
174            {
175                getLogger().debug("The validator refused a missing or empty value for a mandatory parameter");
176            }
177            
178            errors.addError(new I18nizableText("plugin.core-ui", "PLUGINS_CORE_UI_DEFAULT_VALIDATOR_MANDATORY"));
179        }
180        
181        if (_regexp != null && value != null && value.toString().length() != 0 && !_regexp.matcher(value.toString()).matches())
182        {
183            if (getLogger().isDebugEnabled())
184            {
185                getLogger().debug("The validator refused a value for a parameter that should respect a regexep");
186            }
187
188            errors.addError(new I18nizableText("plugin.core-ui", "PLUGINS_CORE_UI_DEFAULT_VALIDATOR_PATTERN_FAILED"));
189        }
190    }
191    
192    /**
193     * Validates a array of values.
194     * @param values the values to validate
195     * @param errors the structure to populate if the validation failed.
196     */
197    protected void validateArrayValues (Object[] values, Errors errors)
198    {
199        if (_isMandatory && (values == null || values.length == 0))
200        {
201            if (getLogger().isDebugEnabled())
202            {
203                getLogger().debug("The validator refused a missing or empty value for a mandatory parameter");
204            }
205            
206            errors.addError(new I18nizableText("plugin.core-ui", "PLUGINS_CORE_UI_DEFAULT_VALIDATOR_MANDATORY"));
207        }
208        
209        if (_regexp != null && values != null && !_matchRegexp(values))
210        {
211            if (getLogger().isDebugEnabled())
212            {
213                getLogger().debug("The validator refused a value for a parameter that should respect a regexep");
214            }
215
216            errors.addError(new I18nizableText("plugin.core-ui", "PLUGINS_CORE_UI_DEFAULT_VALIDATOR_PATTERN_FAILED"));
217        }
218    }
219    
220    private boolean _matchRegexp (Object[] values)
221    {
222        for (Object value : values)
223        {
224            if (!_regexp.matcher(value.toString()).matches())
225            {
226                return false;
227            }
228        }
229        return true;
230    }
231
232    @Override
233    public int hashCode()
234    {
235        final int prime = 31;
236        int result = 1;
237        result = prime * result + ((_invalidText == null) ? 0 : _invalidText.hashCode());
238        result = prime * result + (_isMandatory ? 1231 : 1237);
239        result = prime * result + ((_pluginName == null) ? 0 : _pluginName.hashCode());
240        result = prime * result + ((_regexp == null) ? 0 : _regexp.hashCode());
241        result = prime * result + ((_smanager == null) ? 0 : _smanager.hashCode());
242        return result;
243    }
244    
245    @Override
246    public boolean equals(Object obj)
247    {
248        // Generated method but modified to correctly compare patterns
249        if (this == obj)
250        {
251            return true;
252        }
253        if (obj == null)
254        {
255            return false;
256        }
257        if (getClass() != obj.getClass())
258        {
259            return false;
260        }
261        DefaultValidator other = (DefaultValidator) obj;
262        if (_invalidText == null)
263        {
264            if (other._invalidText != null)
265            {
266                return false;
267            }
268        }
269        else if (!_invalidText.equals(other._invalidText))
270        {
271            return false;
272        }
273        if (_isMandatory != other._isMandatory)
274        {
275            return false;
276        }
277        if (_regexp == null)
278        {
279            if (other._regexp != null)
280            {
281                return false;
282            }
283        }
284        // Beginning of the modification
285        else if (other._regexp == null)
286        {
287            return false;
288        }
289        else if (!_regexp.pattern().equals(other._regexp.pattern()))
290        {
291            return false;
292        }
293        // End of the modification
294        return true;
295    }
296}