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.lang3.StringUtils;
028
029import org.ametys.runtime.i18n.I18nizableText;
030import org.ametys.runtime.parameter.ParameterHelper;
031import org.ametys.runtime.parameter.ValidationResult;
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                        ValidationResult validationResult = pref.getValidator().validate(castValue == null ? value : castValue);
127                        
128                        if (validationResult.hasErrors())
129                        {
130                            errors.addErrors(pref.getId(), validationResult.getErrors());
131                        }
132                    }
133                }
134            }
135        }
136    }
137    
138    /**
139     * Compute the preferences map.
140     * @param contextVars The context variables including environment elements 
141     * @return the preferences map.
142     */
143    protected Map<String, UserPreference> getPreferencesMap(Map<String, String> contextVars)
144    {
145        Map<String, UserPreference> preferences = new HashMap<>();
146        
147        for (String extensionId : getExtensionsIds())
148        {
149            UserPreferenceProvider provider = getExtension(extensionId);
150            
151            for (UserPreference preference : provider.getPreferences(contextVars))
152            {
153                preferences.put(preference.getId(), preference);
154            }
155        }
156        
157        return preferences;
158    }
159    
160    /**
161     * Compute the grouped preferences map.
162     * @param contextVars The context variables including environment elements 
163     * @return the grouped preferences map.
164     */
165    protected Map<I18nizableText, List<UserPreference>> getCategorizedPreferencesMap(Map<String, String> contextVars)
166    {
167        Map<I18nizableText, List<UserPreference>> preferences = new TreeMap<>(new I18nizableTextComparator());
168        
169        for (String extensionId : getExtensionsIds())
170        {
171            UserPreferenceProvider provider = getExtension(extensionId);
172            
173            for (UserPreference preference : provider.getPreferences(contextVars))
174            {
175                I18nizableText groupName = preference.getDisplayGroup();
176
177                // Get the map of preferences of the group.
178                List<UserPreference> group = preferences.get(groupName);
179                if (group == null)
180                {
181                    group = new ArrayList<>();
182                    preferences.put(groupName, group);
183                }
184                
185                group.add(preference);
186            }
187        }
188        
189        // Sort all groups.
190        for (List<UserPreference> group : preferences.values())
191        {
192            Collections.sort(group, _comparator);
193        }
194        
195        return preferences;
196    }
197    
198    /**
199     * Compares user preferences on their "order" attribute.
200     */
201    class UserPrefOrderComparator implements Comparator<UserPreference>
202    {
203        @Override
204        public int compare(UserPreference pref1, UserPreference pref2)
205        {
206            return pref1.getOrder() - pref2.getOrder();
207        }
208    }
209    
210    class I18nizableTextComparator implements Comparator<I18nizableText>
211    {
212        @Override
213        public int compare(I18nizableText t1, I18nizableText t2)
214        {
215            return t1.toString().compareTo(t2.toString());
216        }
217    }
218    
219}