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 java.io.IOException;
019    import java.io.InputStream;
020    import java.io.Reader;
021    
022    import java.net.URL;
023    
024    import java.util.ArrayList;
025    import java.util.List;
026    import java.util.prefs.AbstractPreferences;
027    import java.util.prefs.BackingStoreException;
028    
029    public class IniPreferences extends AbstractPreferences
030    {
031    
032        /** frequently used empty String array */
033        private static final String[] EMPTY = {};
034    
035        /** underlaying <code>Ini</code> implementation */
036        private final Ini _ini;
037    
038        /**
039         * Constructs a new preferences node on top of <code>Ini</code> instance.
040         *
041         * @param ini underlaying <code>Ini</code> instance
042         */
043        public IniPreferences(Ini ini)
044        {
045            super(null, "");
046            _ini = ini;
047        }
048    
049        /**
050         * Constructs a new preferences node based on newly loaded <code>Ini</code> instance.
051         *
052         * This is just a helper constructor, to make simpler constructing <code>IniPreferences</code>
053         * directly from <code>Reader</code>.
054         *
055         * @param input the <code>Reader</code> containing <code>Ini</code> data
056         * @throws IOException if an I/O error occured
057         * @throws InvalidFileFormatException if <code>Ini</code> parsing error occured
058         */
059        public IniPreferences(Reader input) throws IOException, InvalidFileFormatException
060        {
061            super(null, "");
062            _ini = new Ini(input);
063        }
064    
065        /**
066         * Constructs a new preferences node based on newly loaded <code>Ini</code> instance.
067         *
068         * This is just a helper constructor, to make simpler constructing <code>IniPreferences</code>
069         * directly from <code>InputStream</code>.
070         *
071         * @param input the <code>InputStream</code> containing <code>Ini</code> data
072         * @throws IOException if an I/O error occured
073         * @throws InvalidFileFormatException if <code>Ini</code> parsing error occured
074         */
075        public IniPreferences(InputStream input) throws IOException, InvalidFileFormatException
076        {
077            super(null, "");
078            _ini = new Ini(input);
079        }
080    
081        /**
082         * Constructs a new preferences node based on newly loaded <code>Ini</code> instance.
083         *
084         * This is just a helper constructor, to make simpler constructing <code>IniPreferences</code>
085         * directly from <code>URL</code>.
086         *
087         * @param input the <code>URL</code> containing <code>Ini</code> data
088         * @throws IOException if an I/O error occured
089         * @throws InvalidFileFormatException if <code>Ini</code> parsing error occured
090         */
091        public IniPreferences(URL input) throws IOException, InvalidFileFormatException
092        {
093            super(null, "");
094            _ini = new Ini(input);
095        }
096    
097        /**
098         * Provide access to underlaying {@link org.ini4j.Ini} implementation.
099         *
100         * @return <code>Ini</code> implementation
101         */
102        protected Ini getIni()
103        {
104            return _ini;
105        }
106    
107        /**
108         * Implements the <CODE>getSpi</CODE> method as per the specification in
109         * {@link java.util.prefs.AbstractPreferences#getSpi(String)}.
110         *
111         * This implementation doesn't support this operation, so allways throws UnsupportedOperationException.
112         *
113         * @return if the value associated with the specified key at this preference node, or null if there is no association for this key, or the association cannot be determined at this time.
114         * @param key key to getvalue for
115         * @throws UnsupportedOperationException this implementation allways throws this exception
116         */
117        @Override protected String getSpi(String key) throws UnsupportedOperationException
118        {
119            throw new UnsupportedOperationException();
120        }
121    
122        /**
123         * Implements the <CODE>childrenNamesSpi</CODE> method as per the specification in
124         * {@link java.util.prefs.AbstractPreferences#childrenNamesSpi()}.
125         * @return an array containing the names of the children of this preference node.
126         * @throws BackingStoreException if this operation cannot be completed due to a failure in the backing store, or inability to communicate with it.
127         */
128        @Override protected String[] childrenNamesSpi() throws BackingStoreException
129        {
130            List<String> names = new ArrayList<String>();
131    
132            for (String name : _ini.keySet())
133            {
134                if (name.indexOf(_ini.getPathSeparator()) < 0)
135                {
136                    names.add(name);
137                }
138            }
139    
140            return names.toArray(EMPTY);
141        }
142    
143        /**
144         * Implements the <CODE>childSpi</CODE> method as per the specification in
145         * {@link java.util.prefs.AbstractPreferences#childSpi(String)}.
146         * @param name child name
147         * @return child node
148         */
149        @Override protected SectionPreferences childSpi(String name)
150        {
151            Ini.Section sec = _ini.get(name);
152            boolean isNew = sec == null;
153    
154            if (isNew)
155            {
156                sec = _ini.add(name);
157            }
158    
159            return new SectionPreferences(this, sec, isNew);
160        }
161    
162        /**
163         * Implements the <CODE>flushSpi</CODE> method as per the specification in
164         * {@link java.util.prefs.AbstractPreferences#flushSpi()}.
165         *
166         * This implementation does nothing.
167         *
168         * @throws BackingStoreException if this operation cannot be completed due to a failure in the backing store, or inability to communicate with it.
169         */
170        @Override protected void flushSpi() throws BackingStoreException
171        {
172            assert true;
173        }
174    
175        /**
176         * Implements the <CODE>keysSpi</CODE> method as per the specification in
177         * {@link java.util.prefs.AbstractPreferences#keysSpi()}.
178         *
179         * This implementation allways return an empty array.
180         *
181         * @return an empty array.
182         * @throws BackingStoreException if this operation cannot be completed due to a failure in the backing store, or inability to communicate with it.
183         */
184        @Override protected String[] keysSpi() throws BackingStoreException
185        {
186            return EMPTY;
187        }
188    
189        /**
190         * Implements the <CODE>putSpi</CODE> method as per the specification in
191         * {@link java.util.prefs.AbstractPreferences#putSpi(String,String)}.
192         *
193         * This implementation doesn;t support this operation, so allways throws UnsupportedOperationException.
194         *
195         * @param key key to set value for
196         * @param value new value for key
197         * @throws UnsupportedOperationException this implementation allways throws this exception
198         */
199        @Override protected void putSpi(String key, String value) throws UnsupportedOperationException
200        {
201            throw new UnsupportedOperationException();
202        }
203    
204        /**
205         * Implements the <CODE>removeNodeSpi</CODE> method as per the specification in
206         * {@link java.util.prefs.AbstractPreferences#removeNodeSpi()}.
207         *
208         * This implementation doesn;t support this operation, so allways throws UnsupportedOperationException.
209         * @throws UnsupportedOperationException this implementation allways throws this exception
210         * @throws BackingStoreException this implementation never throws this exception
211         */
212        @Override protected void removeNodeSpi() throws BackingStoreException, UnsupportedOperationException
213        {
214            throw new UnsupportedOperationException();
215        }
216    
217        /**
218         * Implements the <CODE>removeSpi</CODE> method as per the specification in
219         * {@link java.util.prefs.AbstractPreferences#removeSpi(String)}.
220         * @param key key to remove
221         * @throws UnsupportedOperationException this implementation allways throws this exception
222         */
223        @Override protected void removeSpi(String key) throws UnsupportedOperationException
224        {
225            throw new UnsupportedOperationException();
226        }
227    
228        /**
229         * Implements the <CODE>syncSpi</CODE> method as per the specification in
230         * {@link java.util.prefs.AbstractPreferences#syncSpi()}.
231         *
232         * This implementation does nothing.
233         *
234         * @throws BackingStoreException if this operation cannot be completed due to a failure in the backing store, or inability to communicate with it.
235         */
236        @Override protected void syncSpi() throws BackingStoreException
237        {
238            assert true;
239        }
240    
241        protected class SectionPreferences extends AbstractPreferences
242        {
243    
244            /** underlaying <code>Section</code> implementation */
245            private final Ini.Section _section;
246    
247            /**
248             * Constructs a new SectionPreferences instance on top of Ini.Section instance.
249             *
250             * @param parent parent preferences node
251             * @parem section underlaying Ini.Section instance
252             * @param isNew indicate is this a new node or already existing one
253             */
254            SectionPreferences(AbstractPreferences parent, Ini.Section section, boolean isNew)
255            {
256                super(parent, section.getSimpleName());
257                _section = section;
258                newNode = isNew;
259            }
260    
261            /**
262             * Implements the <CODE>flush</CODE> method as per the specification in
263             * {@link java.util.prefs.Preferences#flush()}.
264             *
265             * This implementation just call parent's <code>flush()</code> method.
266             *
267             * @throws BackingStoreException if this operation cannot be completed due to a failure in the backing store, or inability to communicate with it.
268             */
269            @Override public void flush() throws BackingStoreException
270            {
271                parent().flush();
272            }
273    
274            /**
275             * Implements the <CODE>sync</CODE> method as per the specification in
276             * {@link java.util.prefs.Preferences#sync()}.
277             *
278             * This implementation just call parent's <code>sync()</code> method.
279             *
280             * @throws BackingStoreException if this operation cannot be completed due to a failure in the backing store, or inability to communicate with it.
281             */
282            @Override public void sync() throws BackingStoreException
283            {
284                parent().sync();
285            }
286    
287            /**
288             * Implements the <CODE>getSpi</CODE> method as per the specification in
289             * {@link java.util.prefs.AbstractPreferences#getSpi(String)}.
290             * @return if the value associated with the specified key at this preference node, or null if there is no association for this key, or the association cannot be determined at this time.
291             * @param key key to getvalue for
292             */
293            @Override protected String getSpi(String key)
294            {
295                return _section.fetch(key);
296            }
297    
298            /**
299             * Implements the <CODE>childrenNamesSpi</CODE> method as per the specification in
300             * {@link java.util.prefs.AbstractPreferences#childrenNamesSpi()}.
301             *
302             * This implementation allways returns an empty array.
303             *
304             * @return an emty array.
305             * @throws BackingStoreException if this operation cannot be completed due to a failure in the backing store, or inability to communicate with it.
306             */
307            @Override protected String[] childrenNamesSpi() throws BackingStoreException
308            {
309                return _section.childrenNames();
310            }
311    
312            /**
313             * Implements the <CODE>childSpi</CODE> method as per the specification in
314             * {@link java.util.prefs.AbstractPreferences#childSpi(String)}.
315             *
316             * This implementation doesn't support this operation.
317             *
318             * @throws UnsupportedOperationException this implementation allways throws this exception
319             * @param name child name
320             * @return child node
321             */
322            @Override protected SectionPreferences childSpi(String name) throws UnsupportedOperationException
323            {
324                Ini.Section child = _section.getChild(name);
325                boolean isNew = child == null;
326    
327                if (isNew)
328                {
329                    child = _section.addChild(name);
330                }
331    
332                return new SectionPreferences(this, child, isNew);
333            }
334    
335            /**
336             * Implements the <CODE>flushSpi</CODE> method as per the specification in
337             * {@link java.util.prefs.AbstractPreferences#flushSpi()}.
338             *
339             * This implementation does nothing.
340             *
341             * @throws BackingStoreException if this operation cannot be completed due to a failure in the backing store, or inability to communicate with it.
342             */
343            @Override protected void flushSpi() throws BackingStoreException
344            {
345                assert true;
346            }
347    
348            /**
349             * Implements the <CODE>keysSpi</CODE> method as per the specification in
350             * {@link java.util.prefs.AbstractPreferences#keysSpi()}.
351             *
352             * @return an array of the keys that have an associated value in this preference node.
353             * @throws BackingStoreException if this operation cannot be completed due to a failure in the backing store, or inability to communicate with it.
354             */
355            @Override protected String[] keysSpi() throws BackingStoreException
356            {
357                return _section.keySet().toArray(EMPTY);
358            }
359    
360            /**
361             * Implements the <CODE>putSpi</CODE> method as per the specification in
362             * {@link java.util.prefs.AbstractPreferences#putSpi(String,String)}.
363             *
364             * @param key key to set value for
365             * @param value new value of key
366             */
367            @Override protected void putSpi(String key, String value)
368            {
369                _section.put(key, value);
370            }
371    
372            /**
373             * Implements the <CODE>removeNodeSpi</CODE> method as per the specification in
374             * {@link java.util.prefs.AbstractPreferences#removeNodeSpi()}.
375             *
376             * @throws BackingStoreException if this operation cannot be completed due to a failure in the backing store, or inability to communicate with it.
377             */
378            @Override protected void removeNodeSpi() throws BackingStoreException
379            {
380                _ini.remove(_section);
381            }
382    
383            /**
384             * Implements the <CODE>removeSpi</CODE> method as per the specification in
385             * {@link java.util.prefs.AbstractPreferences#removeSpi(String)}.
386             * @param key key to remove
387             */
388            @Override protected void removeSpi(String key)
389            {
390                _section.remove(key);
391            }
392    
393            /**
394             * Implements the <CODE>syncSpi</CODE> method as per the specification in
395             * {@link java.util.prefs.AbstractPreferences#syncSpi()}.
396             *
397             * This implementation does nothing.
398             *
399             * @throws BackingStoreException if this operation cannot be completed due to a failure in the backing store, or inability to communicate with it.
400             */
401            @Override protected void syncSpi() throws BackingStoreException
402            {
403                assert true;
404            }
405        }
406    }