001    /*
002     * Copyright 2005,2009 Ivan SZKIBA
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     */
016    package org.ini4j;
017    
018    import org.ini4j.spi.BeanAccess;
019    import org.ini4j.spi.BeanTool;
020    import org.ini4j.spi.Warnings;
021    
022    import java.lang.reflect.Array;
023    
024    import java.util.regex.Matcher;
025    import java.util.regex.Pattern;
026    
027    public class BasicOptionMap extends CommonMultiMap<String, String> implements OptionMap
028    {
029        private static final char SUBST_CHAR = '$';
030        private static final String SYSTEM_PROPERTY_PREFIX = "@prop/";
031        private static final String ENVIRONMENT_PREFIX = "@env/";
032        private static final int SYSTEM_PROPERTY_PREFIX_LEN = SYSTEM_PROPERTY_PREFIX.length();
033        private static final int ENVIRONMENT_PREFIX_LEN = ENVIRONMENT_PREFIX.length();
034        private static final Pattern EXPRESSION = Pattern.compile("(?<!\\\\)\\$\\{(([^\\[]+)(\\[([0-9]+)\\])?)\\}");
035        private static final int G_OPTION = 2;
036        private static final int G_INDEX = 4;
037        private static final long serialVersionUID = 325469712293707584L;
038        private BeanAccess _defaultBeanAccess;
039        private final boolean _propertyFirstUpper;
040    
041        public BasicOptionMap()
042        {
043            this(false);
044        }
045    
046        public BasicOptionMap(boolean propertyFirstUpper)
047        {
048            _propertyFirstUpper = propertyFirstUpper;
049        }
050    
051        @Override @SuppressWarnings(Warnings.UNCHECKED)
052        public <T> T getAll(Object key, Class<T> clazz)
053        {
054            requireArray(clazz);
055            T value;
056    
057            value = (T) Array.newInstance(clazz.getComponentType(), length(key));
058            for (int i = 0; i < length(key); i++)
059            {
060                Array.set(value, i, BeanTool.getInstance().parse(get(key, i), clazz.getComponentType()));
061            }
062    
063            return value;
064        }
065    
066        @Override public void add(String key, Object value)
067        {
068            super.add(key, ((value == null) || (value instanceof String)) ? (String) value : String.valueOf(value));
069        }
070    
071        @Override public void add(String key, Object value, int index)
072        {
073            super.add(key, ((value == null) || (value instanceof String)) ? (String) value : String.valueOf(value), index);
074        }
075    
076        @Override public <T> T as(Class<T> clazz)
077        {
078            return BeanTool.getInstance().proxy(clazz, getDefaultBeanAccess());
079        }
080    
081        @Override public <T> T as(Class<T> clazz, String keyPrefix)
082        {
083            return BeanTool.getInstance().proxy(clazz, newBeanAccess(keyPrefix));
084        }
085    
086        @Override public String fetch(Object key)
087        {
088            int len = length(key);
089    
090            return (len == 0) ? null : fetch(key, len - 1);
091        }
092    
093        @Override public String fetch(Object key, int index)
094        {
095            String value = get(key, index);
096    
097            if ((value != null) && (value.indexOf(SUBST_CHAR) >= 0))
098            {
099                StringBuilder buffer = new StringBuilder(value);
100    
101                resolve(buffer);
102                value = buffer.toString();
103            }
104    
105            return value;
106        }
107    
108        @Override public <T> T fetch(Object key, Class<T> clazz)
109        {
110            return BeanTool.getInstance().parse(fetch(key), clazz);
111        }
112    
113        @Override public <T> T fetch(Object key, int index, Class<T> clazz)
114        {
115            return BeanTool.getInstance().parse(fetch(key, index), clazz);
116        }
117    
118        @Override @SuppressWarnings(Warnings.UNCHECKED)
119        public <T> T fetchAll(Object key, Class<T> clazz)
120        {
121            requireArray(clazz);
122            T value;
123    
124            value = (T) Array.newInstance(clazz.getComponentType(), length(key));
125            for (int i = 0; i < length(key); i++)
126            {
127                Array.set(value, i, BeanTool.getInstance().parse(fetch(key, i), clazz.getComponentType()));
128            }
129    
130            return value;
131        }
132    
133        @Override public void from(Object bean)
134        {
135            BeanTool.getInstance().inject(getDefaultBeanAccess(), bean);
136        }
137    
138        @Override public void from(Object bean, String keyPrefix)
139        {
140            BeanTool.getInstance().inject(newBeanAccess(keyPrefix), bean);
141        }
142    
143        @Override public <T> T get(Object key, Class<T> clazz)
144        {
145            return BeanTool.getInstance().parse(get(key), clazz);
146        }
147    
148        @Override public <T> T get(Object key, int index, Class<T> clazz)
149        {
150            return BeanTool.getInstance().parse(get(key, index), clazz);
151        }
152    
153        @Override public String put(String key, Object value)
154        {
155            return super.put(key, ((value == null) || (value instanceof String)) ? (String) value : String.valueOf(value));
156        }
157    
158        @Override public String put(String key, Object value, int index)
159        {
160            return super.put(key, ((value == null) || (value instanceof String)) ? (String) value : String.valueOf(value), index);
161        }
162    
163        @Override public void putAll(String key, Object value)
164        {
165            if (value != null)
166            {
167                requireArray(value.getClass());
168            }
169    
170            remove(key);
171            if (value != null)
172            {
173                int n = Array.getLength(value);
174    
175                for (int i = 0; i < n; i++)
176                {
177                    add(key, Array.get(value, i));
178                }
179            }
180        }
181    
182        @Override public void to(Object bean)
183        {
184            BeanTool.getInstance().inject(bean, getDefaultBeanAccess());
185        }
186    
187        @Override public void to(Object bean, String keyPrefix)
188        {
189            BeanTool.getInstance().inject(bean, newBeanAccess(keyPrefix));
190        }
191    
192        synchronized BeanAccess getDefaultBeanAccess()
193        {
194            if (_defaultBeanAccess == null)
195            {
196                _defaultBeanAccess = newBeanAccess();
197            }
198    
199            return _defaultBeanAccess;
200        }
201    
202        boolean isPropertyFirstUpper()
203        {
204            return _propertyFirstUpper;
205        }
206    
207        BeanAccess newBeanAccess()
208        {
209            return new Access();
210        }
211    
212        BeanAccess newBeanAccess(String propertyNamePrefix)
213        {
214            return new Access(propertyNamePrefix);
215        }
216    
217        void resolve(StringBuilder buffer)
218        {
219            Matcher m = EXPRESSION.matcher(buffer);
220    
221            while (m.find())
222            {
223                String name = m.group(G_OPTION);
224                int index = (m.group(G_INDEX) == null) ? -1 : Integer.parseInt(m.group(G_INDEX));
225                String value;
226    
227                if (name.startsWith(ENVIRONMENT_PREFIX))
228                {
229                    value = Config.getEnvironment(name.substring(ENVIRONMENT_PREFIX_LEN));
230                }
231                else if (name.startsWith(SYSTEM_PROPERTY_PREFIX))
232                {
233                    value = Config.getSystemProperty(name.substring(SYSTEM_PROPERTY_PREFIX_LEN));
234                }
235                else
236                {
237                    value = (index == -1) ? fetch(name) : fetch(name, index);
238                }
239    
240                if (value != null)
241                {
242                    buffer.replace(m.start(), m.end(), value);
243                    m.reset(buffer);
244                }
245            }
246        }
247    
248        private void requireArray(Class clazz)
249        {
250            if (!clazz.isArray())
251            {
252                throw new IllegalArgumentException("Array required");
253            }
254        }
255    
256        class Access implements BeanAccess
257        {
258            private final String _prefix;
259    
260            Access()
261            {
262                this(null);
263            }
264    
265            Access(String prefix)
266            {
267                _prefix = prefix;
268            }
269    
270            @Override public void propAdd(String propertyName, String value)
271            {
272                add(transform(propertyName), value);
273            }
274    
275            @Override public String propDel(String propertyName)
276            {
277                return remove(transform(propertyName));
278            }
279    
280            @Override public String propGet(String propertyName)
281            {
282                return fetch(transform(propertyName));
283            }
284    
285            @Override public String propGet(String propertyName, int index)
286            {
287                return fetch(transform(propertyName), index);
288            }
289    
290            @Override public int propLength(String propertyName)
291            {
292                return length(transform(propertyName));
293            }
294    
295            @Override public String propSet(String propertyName, String value)
296            {
297                return put(transform(propertyName), value);
298            }
299    
300            @Override public String propSet(String propertyName, String value, int index)
301            {
302                return put(transform(propertyName), value, index);
303            }
304    
305            private String transform(String orig)
306            {
307                String ret = orig;
308    
309                if (((_prefix != null) || isPropertyFirstUpper()) && (orig != null))
310                {
311                    StringBuilder buff = new StringBuilder();
312    
313                    if (_prefix != null)
314                    {
315                        buff.append(_prefix);
316                    }
317    
318                    if (isPropertyFirstUpper())
319                    {
320                        buff.append(Character.toUpperCase(orig.charAt(0)));
321                        buff.append(orig.substring(1));
322                    }
323                    else
324                    {
325                        buff.append(orig);
326                    }
327    
328                    ret = buff.toString();
329                }
330    
331                return ret;
332            }
333        }
334    }