/* Copyright 2011-2012 Opera Software ASA Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package com.opera.core.systems.preferences; import com.google.common.base.Charsets; import com.google.common.base.Joiner; import com.google.common.base.Predicate; import com.google.common.base.Throwables; import com.google.common.collect.Iterables; import com.google.common.io.Files; import org.ini4j.Config; import org.ini4j.Ini; import org.ini4j.Profile; import org.ini4j.Wini; import org.openqa.selenium.WebDriverException; import java.io.File; import java.io.IOException; import java.io.StringReader; import java.util.List; import java.util.Map; /** * OperaFilePreferences allows updating preferences in an Opera preference file such as * <code>opera.ini</code> or <code>operaprefs.ini</code>. The file will be written to upon each * alteration to any preference. */ public class OperaFilePreferences extends AbstractOperaPreferences { private File preferenceFile; /** * Constructs a new representation of Opera's preferences based on the given preference file. * * @param preferenceFile an INI style preference file */ public OperaFilePreferences(File preferenceFile) { this.preferenceFile = preferenceFile; // Create new preference file if it doesn't exist if (!preferenceFile.exists()) { try { if (!preferenceFile.createNewFile()) { throw new IOException("File exists"); } } catch (IOException e) { throw new WebDriverException("Unable to create new preference file: " + e.getMessage()); } return; } Ini ini = getIniForPreferenceFile(preferenceFile); // Add each preference entry for (Map.Entry<String, Profile.Section> section : ini.entrySet()) { for (Map.Entry<String, String> entry : section.getValue().entrySet()) { set(section.getValue().getName(), entry.getKey(), entry.getValue()); } } } private Ini getIniForPreferenceFile(File preferenceFile) { // Due to the sucky nature of Opera's invalid preference files, we are forced to remove the // first line of the file. // // opera.ini looks much like this: // // <BOM> // Opera Preferences version 2.1 // ; Do not edit this file while Opera is running // ; This file is stored in UTF-8 encoding // // [User Prefs] // Language Files Directory= // // &c. try { List<String> lines = Files.readLines(preferenceFile, Charsets.UTF_8); Iterable<String> filteredLines = Iterables.filter(lines, new Predicate<String>() { public boolean apply(String line) { return !line.contains("Opera Preferences version"); } }); Config config = new Config(); // This config setting makes sure we can handle pref lines without // '=' chars in them. Such prefs will be treated as having value null. config.setEmptyOption(true); // This config setting makes sure we can handle pref lines with '\' in them. config.setEscape(false); Ini ini = new Ini(); ini.setConfig(config); ini.load(new StringReader(Joiner.on("\n").join(filteredLines))); return ini; } catch (IOException e) { throw Throwables.propagate(e); } } public void set(String section, String key, Object value) { set(new FilePreference(this, section, key, value)); } public void set(OperaPreference preference) { if (!(preference instanceof FilePreference)) { super.set(FilePreference.convert(this, preference)); } else { super.set(preference); } write(); } /** * Call this to cause the current preferences representation to be written to disk. This method * is called by {@link FilePreference#setValue(Object)} and it is thus not necessary to call this * method separately unless you wish to perform a forced write of the cache to disk. */ public void write() { try { Wini ini = new Wini(preferenceFile); for (OperaPreference p : this) { ini.put(p.getSection(), p.getKey(), ((AbstractPreference) p).getValue(true)); } ini.store(preferenceFile); } catch (IOException e) { throw new WebDriverException("Unable to write to preference file: " + e.getMessage()); } } /** * Allows setting Opera preferences in a preference file (typically <code>opera.ini</code> or * <code>operaprefs.ini</code>) as well as keeping the local individual preference cache * up-to-date. */ public static class FilePreference extends AbstractPreference { private OperaFilePreferences parent; public FilePreference(OperaFilePreferences parent, String section, String key, Object value) { super(section, key, value); this.parent = parent; } /** * Sets the value of this preference to the given value. Writes the preference to file * immediately after updating the local cache. * * @param value the new value */ public void setValue(Object value) { super.setValue(value); parent.write(); } public static FilePreference convert(OperaFilePreferences parent, OperaPreferences.OperaPreference convertee) { return new FilePreference(parent, convertee.getSection(), convertee.getKey(), convertee.getValue()); } } }