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