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    }