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 }