001/*
002@license.text@
003 */
004package com.hammurapi.common;
005
006import java.io.IOException;
007import java.io.InputStream;
008import java.net.URL;
009import java.util.HashMap;
010import java.util.Locale;
011import java.util.Map;
012import java.util.Properties;
013
014import com.hammurapi.convert.ConversionException;
015import com.hammurapi.convert.ConvertingService;
016
017
018public class ResourceLoaderImpl implements ResourceLoader {
019
020        private ResourceSet resourceSet;
021        private String[] resolutionPaths;
022        
023        public ResourceLoaderImpl(ResourceSet resourceSet, String... resolutionPaths) {
024                this.resourceSet = resourceSet;
025                this.resolutionPaths = resolutionPaths;
026        }
027        
028        /**
029         * 
030         * @param profile Profile, can be null
031         * @param extension Extension, can be null
032         * @return
033         */
034        public InputStream getResourceStream(String scriptName, String extension) {
035                return getResourceStream(scriptName, Locale.getDefault(), extension);
036        }
037        
038        @Override
039        public URL getResource(String scriptName, String extension) {
040                return getResource(scriptName, Locale.getDefault(), extension);
041        }
042        
043        private enum Variant {
044                LOCALE, LANG_COUNTRY, LANG, GENERIC 
045        }
046
047        /**
048         * @param scriptName
049         * @param extension
050         * @return
051         */
052        public InputStream getResourceStream(final String scriptName, final Locale locale, final String extension) {
053                Locale actualLocale = locale==null ? Locale.getDefault() : locale;
054                for (String path: resolutionPaths) {
055                        for (Variant v: Variant.values()) {
056                                String variant=path;
057                                if (scriptName!=null) {
058                                        variant+="!"+scriptName;
059                                }
060                                
061                                switch (v) {
062                                case LOCALE:
063                                        variant+="_"+actualLocale;
064                                        break;
065                                case LANG_COUNTRY:
066                                        variant+="_"+actualLocale.getLanguage();
067                                        if (actualLocale.getCountry().length()!=0) {
068                                                variant+="_"+actualLocale.getCountry();
069                                        }
070                                        break;
071                                case LANG:
072                                        variant+="_"+actualLocale.getLanguage();
073                                        break;
074                                case GENERIC:
075                                        break;  
076                                }
077                                
078                                if (extension!=null) {
079                                        variant+="."+extension;
080                                }
081                                                        
082                                InputStream ret = resourceSet.getResourceStream(variant);
083                                if (ret!=null) {
084                                        return ret;
085                                }
086                        }                       
087                }
088
089                return null;
090        }
091
092        /**
093         * @param scriptName
094         * @param extension
095         * @return
096         */
097        public URL getResource(final String scriptName, final Locale locale, final String extension) {
098                Locale actualLocale = locale==null ? Locale.getDefault() : locale;
099                for (String path: resolutionPaths) {
100                        for (Variant v: Variant.values()) {
101                                String variant=path;
102                                if (scriptName!=null) {
103                                        variant+="!"+scriptName;
104                                }
105                                
106                                switch (v) {
107                                case LOCALE:
108                                        variant+="_"+actualLocale;
109                                        break;
110                                case LANG_COUNTRY:
111                                        variant+="_"+actualLocale.getLanguage();
112                                        if (actualLocale.getCountry().length()!=0) {
113                                                variant+="_"+actualLocale.getCountry();
114                                        }
115                                        break;
116                                case LANG:
117                                        variant+="_"+actualLocale.getLanguage();
118                                        break;
119                                case GENERIC:
120                                        break;  
121                                }
122                                
123                                if (extension!=null) {
124                                        variant+="."+extension;
125                                }
126                                                        
127                                URL ret = resourceSet.getResource(variant);
128                                if (ret!=null) {
129                                        return ret;
130                                }
131                        }                       
132                }
133
134                return null;
135        }
136
137        /**
138         * Collects properties from class hierarchy.  
139         * @param profile Profile, can be null
140         * @param extension Extension, can be null
141         * @return
142         */
143        public Map<String, Object> getProperties(String profile, String extension) {
144                return getProperties(profile, Locale.getDefault(), extension);
145        }
146        
147        /**
148         * Collects properties from class hierarchy.  
149         * @param profile
150         * @param extension
151         * @return
152         */
153        public Map<String, Object> getProperties(final String profile, final Locale locale, final String extension) {                             
154                return getProperties(profile, locale, extension, Thread.currentThread().getContextClassLoader());
155        }
156        
157        /**
158         * Collects properties from class hierarchy. Property can have type. Type name is enclosed into parenthesis, e.g.
159         * <code>attempts(java.lang.Integer)=33</code>. If property is defined with type then string value of the property is converted to the
160         * target type. If not, property type is String. 
161         * @param profile
162         * @param extension
163         * @return
164         */
165        public Map<String, Object> getProperties(final String profile, final Locale locale, final String extension, ClassLoader classLoader) {                            
166                Map<String, Object> ret = new HashMap<String, Object>(); 
167                Locale actualLocale = locale==null ? Locale.getDefault() : locale;
168                for (String path: resolutionPaths) {
169                        for (Variant v: Variant.values()) {
170                                String variant=path;
171                                if (profile!=null) {
172                                        variant+="!"+profile;
173                                }
174                                
175                                switch (v) {
176                                case LOCALE:
177                                        variant+="_"+actualLocale;
178                                        break;
179                                case LANG_COUNTRY:
180                                        variant+="_"+actualLocale.getLanguage();
181                                        if (actualLocale.getCountry().length()!=0) {
182                                                variant+="_"+actualLocale.getCountry();
183                                        }
184                                        break;
185                                case LANG:
186                                        variant+="_"+actualLocale.getLanguage();
187                                        break;
188                                case GENERIC:
189                                        break;
190                                }
191                                if (extension!=null) {
192                                        variant+="."+extension;
193                                }
194                                                        
195                                InputStream s = resourceSet.getResourceStream(variant);
196                                if (s!=null) {
197                                        Properties cp=new Properties();
198                                        try {
199                                                cp.load(s);
200                                                for (Map.Entry<Object, Object> entry: cp.entrySet()) {
201                                                        String key = (String) entry.getKey();
202                                                        Object value = entry.getValue();                                                        
203                                                        int idx = key.charAt(key.length()-1)==')' ? key.lastIndexOf('(') : -1;
204                                                        String propertyClassName = null;
205                                                        if (idx>0) {
206                                                                propertyClassName = key.substring(idx+1, key.length()-1);
207                                                                key = key.substring(0, idx);                                                            
208                                                        }
209                                                        if (!ret.containsKey(key)) {
210                                                                if (propertyClassName!=null) {
211                                                                        try {
212                                                                                Class<?> propertyClass = classLoader.loadClass(propertyClassName);
213                                                                                value = ConvertingService.convert(value, propertyClass, classLoader);
214                                                                        } catch (ClassNotFoundException e) {
215                                                                                throw new ConversionException(e);
216                                                                        }
217                                                                }
218                                                                ret.put(key, value);                                                    
219                                                        }                                               
220                                                }
221                                        } catch (IOException e) {
222                                                throw new RuntimeException("Cannot load properties from resource "+variant, e);
223                                        }
224                                        
225                                        break;
226                                }
227                        }                       
228                }
229                                
230                
231                return ret;
232        }
233}