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 */
016package org.ini4j;
017
018import java.io.IOException;
019import java.io.InputStream;
020import java.io.Reader;
021
022import java.net.URL;
023
024import java.util.ArrayList;
025import java.util.List;
026import java.util.prefs.AbstractPreferences;
027import java.util.prefs.BackingStoreException;
028
029public 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}