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 org.ini4j.spi.IniFormatter;
019 import org.ini4j.spi.IniHandler;
020 import org.ini4j.spi.IniParser;
021 import org.ini4j.spi.RegBuilder;
022
023 import java.io.File;
024 import java.io.FileNotFoundException;
025 import java.io.FileOutputStream;
026 import java.io.IOException;
027 import java.io.InputStream;
028 import java.io.InputStreamReader;
029 import java.io.InterruptedIOException;
030 import java.io.OutputStream;
031 import java.io.OutputStreamWriter;
032 import java.io.Reader;
033 import java.io.Writer;
034
035 import java.net.URL;
036
037 public class Reg extends BasicRegistry implements Registry, Persistable, Configurable
038 {
039 private static final long serialVersionUID = -1485602876922985912L;
040 protected static final String DEFAULT_SUFFIX = ".reg";
041 protected static final String TMP_PREFIX = "reg-";
042 private static final int STDERR_BUFF_SIZE = 8192;
043 private static final String PROP_OS_NAME = "os.name";
044 private static final boolean WINDOWS = Config.getSystemProperty(PROP_OS_NAME, "Unknown").startsWith("Windows");
045 private static final char CR = '\r';
046 private static final char LF = '\n';
047 private Config _config;
048 private File _file;
049
050 public Reg()
051 {
052 Config cfg = Config.getGlobal().clone();
053
054 cfg.setEscape(false);
055 cfg.setGlobalSection(false);
056 cfg.setEmptyOption(true);
057 cfg.setMultiOption(true);
058 cfg.setStrictOperator(true);
059 cfg.setEmptySection(true);
060 cfg.setPathSeparator(KEY_SEPARATOR);
061 cfg.setFileEncoding(FILE_ENCODING);
062 cfg.setLineSeparator(LINE_SEPARATOR);
063 _config = cfg;
064 }
065
066 public Reg(String registryKey) throws IOException
067 {
068 this();
069 read(registryKey);
070 }
071
072 public Reg(File input) throws IOException, InvalidFileFormatException
073 {
074 this();
075 _file = input;
076 load();
077 }
078
079 public Reg(URL input) throws IOException, InvalidFileFormatException
080 {
081 this();
082 load(input);
083 }
084
085 public Reg(InputStream input) throws IOException, InvalidFileFormatException
086 {
087 this();
088 load(input);
089 }
090
091 public Reg(Reader input) throws IOException, InvalidFileFormatException
092 {
093 this();
094 load(input);
095 }
096
097 public static boolean isWindows()
098 {
099 return WINDOWS;
100 }
101
102 @Override public Config getConfig()
103 {
104 return _config;
105 }
106
107 public void setConfig(Config value)
108 {
109 _config = value;
110 }
111
112 @Override public File getFile()
113 {
114 return _file;
115 }
116
117 @Override public void setFile(File value)
118 {
119 _file = value;
120 }
121
122 @Override public void load() throws IOException, InvalidFileFormatException
123 {
124 if (_file == null)
125 {
126 throw new FileNotFoundException();
127 }
128
129 load(_file);
130 }
131
132 @Override public void load(InputStream input) throws IOException, InvalidFileFormatException
133 {
134 load(new InputStreamReader(input, getConfig().getFileEncoding()));
135 }
136
137 @Override public void load(URL input) throws IOException, InvalidFileFormatException
138 {
139 load(new InputStreamReader(input.openStream(), getConfig().getFileEncoding()));
140 }
141
142 @Override public void load(Reader input) throws IOException, InvalidFileFormatException
143 {
144 int newline = 2;
145 StringBuilder buff = new StringBuilder();
146
147 for (int c = input.read(); c != -1; c = input.read())
148 {
149 if (c == LF)
150 {
151 newline--;
152 if (newline == 0)
153 {
154 break;
155 }
156 }
157 else if ((c != CR) && (newline != 1))
158 {
159 buff.append((char) c);
160 }
161 }
162
163 if (buff.length() == 0)
164 {
165 throw new InvalidFileFormatException("Missing version header");
166 }
167
168 if (!buff.toString().equals(getVersion()))
169 {
170 throw new InvalidFileFormatException("Unsupported version: " + buff.toString());
171 }
172
173 IniParser.newInstance(getConfig()).parse(input, newBuilder());
174 }
175
176 @Override public void load(File input) throws IOException, InvalidFileFormatException
177 {
178 load(input.toURI().toURL());
179 }
180
181 public void read(String registryKey) throws IOException
182 {
183 File tmp = createTempFile();
184
185 try
186 {
187 regExport(registryKey, tmp);
188 load(tmp);
189 }
190 finally
191 {
192 tmp.delete();
193 }
194 }
195
196 @Override public void store() throws IOException
197 {
198 if (_file == null)
199 {
200 throw new FileNotFoundException();
201 }
202
203 store(_file);
204 }
205
206 @Override public void store(OutputStream output) throws IOException
207 {
208 store(new OutputStreamWriter(output, getConfig().getFileEncoding()));
209 }
210
211 @Override public void store(Writer output) throws IOException
212 {
213 output.write(getVersion());
214 output.write(getConfig().getLineSeparator());
215 output.write(getConfig().getLineSeparator());
216 store(IniFormatter.newInstance(output, getConfig()));
217 }
218
219 @Override public void store(File output) throws IOException
220 {
221 OutputStream stream = new FileOutputStream(output);
222
223 store(stream);
224 stream.close();
225 }
226
227 public void write() throws IOException
228 {
229 File tmp = createTempFile();
230
231 try
232 {
233 store(tmp);
234 regImport(tmp);
235 }
236 finally
237 {
238 tmp.delete();
239 }
240 }
241
242 protected IniHandler newBuilder()
243 {
244 return RegBuilder.newInstance(this);
245 }
246
247 @Override boolean isTreeMode()
248 {
249 return getConfig().isTree();
250 }
251
252 @Override char getPathSeparator()
253 {
254 return getConfig().getPathSeparator();
255 }
256
257 @Override boolean isPropertyFirstUpper()
258 {
259 return getConfig().isPropertyFirstUpper();
260 }
261
262 void exec(String[] args) throws IOException
263 {
264 Process proc = Runtime.getRuntime().exec(args);
265
266 try
267 {
268 int status = proc.waitFor();
269
270 if (status != 0)
271 {
272 Reader in = new InputStreamReader(proc.getErrorStream());
273 char[] buff = new char[STDERR_BUFF_SIZE];
274 int n = in.read(buff);
275
276 in.close();
277 throw new IOException(new String(buff, 0, n).trim());
278 }
279 }
280 catch (InterruptedException x)
281 {
282 throw (IOException) (new InterruptedIOException().initCause(x));
283 }
284 }
285
286 private File createTempFile() throws IOException
287 {
288 File ret = File.createTempFile(TMP_PREFIX, DEFAULT_SUFFIX);
289
290 ret.deleteOnExit();
291
292 return ret;
293 }
294
295 private void regExport(String registryKey, File file) throws IOException
296 {
297 requireWindows();
298 exec(new String[] { "cmd", "/c", "reg", "export", registryKey, file.getAbsolutePath() });
299 }
300
301 private void regImport(File file) throws IOException
302 {
303 requireWindows();
304 exec(new String[] { "cmd", "/c", "reg", "import", file.getAbsolutePath() });
305 }
306
307 private void requireWindows()
308 {
309 if (!WINDOWS)
310 {
311 throw new UnsupportedOperationException("Unsupported operating system or runtime environment");
312 }
313 }
314 }