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.core.userpref;
017
018import java.util.ArrayList;
019import java.util.Collections;
020import java.util.Comparator;
021import java.util.HashMap;
022import java.util.List;
023import java.util.Map;
024import java.util.Map.Entry;
025import java.util.TreeMap;
026
027import org.apache.commons.lang.StringUtils;
028
029import org.ametys.runtime.i18n.I18nizableText;
030import org.ametys.runtime.parameter.Errors;
031import org.ametys.runtime.parameter.ParameterHelper;
032import org.ametys.runtime.parameter.Validator;
033import org.ametys.runtime.plugin.component.AbstractThreadSafeComponentExtensionPoint;
034
035/**
036 * Extension point holding all {@link UserPreference} definitions.
037 */
038public class UserPreferencesExtensionPoint extends AbstractThreadSafeComponentExtensionPoint<UserPreferenceProvider>
039{
040    
041    /** Avalon Role */
042    public static final String ROLE = UserPreferencesExtensionPoint.class.getName();
043    
044    /** User preference parser. */
045    private UserPrefOrderComparator _comparator;
046    
047    @Override
048    public void initialize() throws Exception
049    {
050        super.initialize();
051        
052        _comparator = new UserPrefOrderComparator();
053    }
054    
055    /**
056     * Get all the declared user preferences.
057     * @param contextVars The context variables including environment elements 
058     * @param id The preference id
059     * @return the user preferences (read-only collection).
060     */
061    public UserPreference getUserPreference(Map<String, String> contextVars, String id)
062    {
063        for (String extensionId : getExtensionsIds())
064        {
065            UserPreferenceProvider provider = getExtension(extensionId);
066            
067            for (UserPreference preference : provider.getPreferences(contextVars))
068            {
069                if (preference.getId().equals(id))
070                {
071                    return preference;
072                }
073            }
074        }
075        
076        return null;
077    }
078    
079    /**
080     * Get all the declared user preferences.
081     * @param contextVars The context variables including environment elements 
082     * @return the user preferences (read-only collection).
083     */
084    public Map<String, UserPreference> getUserPreferences(Map<String, String> contextVars)
085    {
086        return Collections.unmodifiableMap(getPreferencesMap(contextVars));
087    }
088    
089    /**
090     * Get all the preferences, classified by group and ordered.
091     * @param contextVars The context variables including environment elements 
092     * @return the preferences classified by group and ordered.
093     */
094    public Map<I18nizableText, List<UserPreference>> getCategorizedPreferences(Map<String, String> contextVars)
095    {
096        return Collections.unmodifiableMap(getCategorizedPreferencesMap(contextVars));
097    }
098    
099    /**
100     * Validate preference values.
101     * @param contextVars The context variables including environment elements 
102     * @param values the values.
103     * @param errors the errors object to fill in.
104     */
105    public void validatePreferences(Map<String, String> contextVars, Map<String, String> values, UserPreferencesErrors errors)
106    {
107        Map<String, UserPreference> preferences = getPreferencesMap(contextVars);
108        
109        for (Entry<String, String> entry : values.entrySet())
110        {
111            UserPreference pref = preferences.get(entry.getKey());
112            if (pref != null)
113            {
114                String value = entry.getValue();
115                
116                Object castValue = ParameterHelper.castValue(value, pref.getType());
117                if (StringUtils.isNotEmpty(value) && castValue == null)
118                {
119                    errors.addError(pref.getId(), new I18nizableText("plugin.core", "PLUGINS_CORE_UI_USER_PREFERENCES_INVALID_TYPE"));
120                }
121                else
122                {
123                    Validator validator = pref.getValidator();
124                    if (validator != null)
125                    {
126                        Errors fieldErrors = new Errors();
127                        pref.getValidator().validate(castValue == null ? value : castValue, fieldErrors);
128                        
129                        if (fieldErrors.hasErrors())
130                        {
131                            errors.addErrors(pref.getId(), fieldErrors.getErrors());
132                        }
133                    }
134                }
135            }
136        }
137    }
138    
139    /**
140     * Compute the preferences map.
141     * @param contextVars The context variables including environment elements 
142     * @return the preferences map.
143     */
144    protected Map<String, UserPreference> getPreferencesMap(Map<String, String> contextVars)
145    {
146        Map<String, UserPreference> preferences = new HashMap<>();
147        
148        for (String extensionId : getExtensionsIds())
149        {
150            UserPreferenceProvider provider = getExtension(extensionId);
151            
152            for (UserPreference preference : provider.getPreferences(contextVars))
153            {
154                preferences.put(preference.getId(), preference);
155            }
156        }
157        
158        return preferences;
159    }
160    
161    /**
162     * Compute the grouped preferences map.
163     * @param contextVars The context variables including environment elements 
164     * @return the grouped preferences map.
165     */
166    protected Map<I18nizableText, List<UserPreference>> getCategorizedPreferencesMap(Map<String, String> contextVars)
167    {
168        Map<I18nizableText, List<UserPreference>> preferences = new TreeMap<>(new I18nizableTextComparator());
169        
170        for (String extensionId : getExtensionsIds())
171        {
172            UserPreferenceProvider provider = getExtension(extensionId);
173            
174            for (UserPreference preference : provider.getPreferences(contextVars))
175            {
176                I18nizableText groupName = preference.getDisplayGroup();
177
178                // Get the map of preferences of the group.
179                List<UserPreference> group = preferences.get(groupName);
180                if (group == null)
181                {
182                    group = new ArrayList<>();
183                    preferences.put(groupName, group);
184                }
185                
186                group.add(preference);
187            }
188        }
189        
190        // Sort all groups.
191        for (List<UserPreference> group : preferences.values())
192        {
193            Collections.sort(group, _comparator);
194        }
195        
196        return preferences;
197    }
198    
199    /**
200     * Compares user preferences on their "order" attribute.
201     */
202    class UserPrefOrderComparator implements Comparator<UserPreference>
203    {
204        @Override
205        public int compare(UserPreference pref1, UserPreference pref2)
206        {
207            return pref1.getOrder() - pref2.getOrder();
208        }
209    }
210    
211    class I18nizableTextComparator implements Comparator<I18nizableText>
212    {
213        @Override
214        public int compare(I18nizableText t1, I18nizableText t2)
215        {
216            return t1.toString().compareTo(t2.toString());
217        }
218    }
219    
220}