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