/******************************************************************************* * Copyright (c) 2009, 2010 Sven Kiera * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html *******************************************************************************/ package org.phpsrc.eclipse.pti.core.php.inifile; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * This class is used for modifying INI file. At the end {@link #close()} must * be called, otherwise the file will be leaved unmodified. * * @author michael */ public class INIFileModifier { private static final String GLOBAL_SECTION = "__global__"; //$NON-NLS-1$ private static final Pattern SECTION_PATTERN = Pattern .compile("\\[([^\\]]+)\\]"); //$NON-NLS-1$ private static final Pattern NAME_VAL_PATTERN = Pattern .compile("([\\w]+)\\s*=\\s*(.*)"); //$NON-NLS-1$ class INIFileSection { String name; List<String> lines; public INIFileSection(String name) { this.name = name; this.lines = new LinkedList<String>(); } } private File configFile; private List<INIFileSection> sections; /** * Create new INI file modifier class instance. If provided INI file doesn't * exist - it will be created. * * @param configFile * INI file path * @throws IOException */ public INIFileModifier(String configFile) throws IOException { this(new File(configFile)); } /** * Create new INI file modifier class instance. If provided INI file doesn't * exist - it will be created. * * @param configFile * INI file object * @throws IOException */ public INIFileModifier(File configFile) throws IOException { this.configFile = configFile; this.sections = new LinkedList<INIFileSection>(); read(); } /** * Adds new entry to the INI file. New entry will be added to the default * (unnamed) section * * @param name * Entry name * @param value * Value name * @param replace * Whether to replace the old entry */ public void addEntry(String name, String value, boolean replace) { addEntry(GLOBAL_SECTION, name, value, replace, null); } /** * Adds new entry to the INI file. New entry will be added to the default * (unnamed) section, no old entries will be replaced * * @param name * Entry name * @param value * Value name */ public void addEntry(String name, String value) { addEntry(GLOBAL_SECTION, name, value, false, null); } /** * Adds new entry to the INI file. New entry will be added to the given * section, no old entries will be replaced * * @param sectionName * Section name * @param name * Entry name * @param value * Value name */ public void addEntry(String sectionName, String name, String value) { addEntry(sectionName, name, value, false, null); } /** * Adds new entry to the INI file. If <code>replace</code> is * <code>true</code> the old entry will be replaced, otherwise - add a new * one. * * @param sectionName * Section name * @param name * Entry name * @param value * Value name * @param replace * Whether to replace the old entry or add a new one * @param replacePattern * Pattern to check against existing entry value, before * replacing it. If <code>replacePattern</code> is * <code>null</code> - every entry that matches the given name * will be replaced */ public void addEntry(String sectionName, String name, String value, boolean replace, String replacePattern) { if (sectionName == null || name == null || value == null) { throw new NullPointerException(); } if (replace) { removeEntry(sectionName, name, replacePattern); } for (INIFileSection section : sections) { if (section.name.equals(sectionName)) { if (replace) { boolean replaced = false; for (int i = 0; i < section.lines.size(); ++i) { Matcher m = NAME_VAL_PATTERN.matcher(section.lines .get(i)); if (m.matches()) { String oldName = m.group(1); String oldValue = m.group(2); if (oldName.equals(name) && (replacePattern == null || oldValue .matches(replacePattern))) { section.lines.set(i, name + '=' + quoteString(value)); replaced = true; break; } } } if (!replaced) { section.lines.add(name + '=' + quoteString(value)); return; } } else { section.lines.add(name + '=' + quoteString(value)); return; } break; } } INIFileSection section = new INIFileSection(sectionName); section.lines.add(name + '=' + quoteString(value)); sections.add(section); } /** * Puts quotes around the given string * * @param str * String * @return result string */ private String quoteString(String str) { if (str.startsWith("\"") && str.endsWith("\"") || str.startsWith("'") && str.endsWith("'")) { return str; } return '"' + str + '"'; } /** * Remove quotes around the given string * * @param str * String * @return result string */ private String removeQuotes(String str) { if (str.startsWith("\"") && str.endsWith("\"") || str.startsWith("'") && str.endsWith("'")) { return str.substring(1, str.length() - 1); } return str; } /** * Removes entry from the INI file from the global section. * * @param name * Entry name * @param removePattern * Pattern to check against existing entry value, before removing * it. If <code>removePattern</code> is <code>null</code> every * entry that matches the given name will be removed. * @return <code>true</code> if some entry was removed, otherwise - false */ public boolean removeEntry(String name, String removePattern) { return removeEntry(GLOBAL_SECTION, name, removePattern); } /** * Removes all entries from the INI file from all sections. Same as * <code>removeEntry(null, name, null)</code>. * * @param name * Entry name * @return <code>true</code> if some entry was removed, otherwise - false * @see #removeEntry(String, String, String) */ public boolean removeAllEntries(String name) { return removeEntry(null, name, null); } /** * Removes all entries from the INI file from all sections. Same as * <code>removeEntry(null, name, removePattern)</code>. * * @param name * Entry name * @param removePattern * Pattern to check against existing entry value, before removing * it. If <code>removePattern</code> is <code>null</code> every * entry that matches the given name will be removed. * * @return <code>true</code> if some entry was removed, otherwise - false * @see #removeEntry(String, String, String) */ public boolean removeAllEntries(String name, String removePattern) { return removeEntry(null, name, removePattern); } /** * Removes entry from the INI file from the given section. * * @param sectionName * Section name. If <code>sectionName</code> is <code>null</code> * , matching entries from all sections will be removed. * * @param name * Entry name * @param removePattern * Pattern to check against existing entry value, before removing * it. If <code>removePattern</code> is <code>null</code> every * entry that matches the given name will be removed. * @return <code>true</code> if some entry was removed, otherwise - false */ public boolean removeEntry(String sectionName, String name, String removePattern) { if (name == null) { throw new NullPointerException(); } boolean removed = false; for (INIFileSection section : sections) { for (int i = 0; i < section.lines.size(); ++i) { Matcher m = NAME_VAL_PATTERN.matcher(section.lines.get(i)); if (m.matches()) { String oldName = m.group(1); String oldValue = m.group(2); if (oldName.equals(name) && (removePattern == null || oldValue .matches(removePattern))) { section.lines.remove(i--); removed = true; } } } } return removed; } /** * Removes entry from the INI file from the global section. * * @param name * Entry name * @param commentPattern * Pattern to check against existing entry value, before removing * it. If <code>commentPattern</code> is <code>null</code> every * entry that matches the given name will be commented. */ public void commentEntry(String name, String commentPattern) { commentEntry(GLOBAL_SECTION, name, commentPattern); } /** * Removes entry from the INI file from all sections. Same as * <code>commentEntry(null, name, commentPattern)</code>. * * @param name * Entry name * @param commentPattern * Pattern to check against existing entry value, before removing * it. If <code>commentPattern</code> is <code>null</code> every * entry that matches the given name will be commented. * * @see #commentEntry(String, String, String) */ public void commentAllEntries(String name, String commentPattern) { commentEntry(null, name, commentPattern); } /** * Removes entry from the INI file from the given section. * * @param sectionName * Section name. If <code>sectionName</code> is <code>null</code> * , matching entries from all sections will be commented. * * @param name * Entry name * @param commentPattern * Pattern to check against existing entry value, before removing * it. If <code>commentPattern</code> is <code>null</code> every * entry that matches the given name will be commented. */ public void commentEntry(String sectionName, String name, String commentPattern) { if (name == null) { throw new NullPointerException(); } for (INIFileSection section : sections) { if (sectionName == null || section.name.equals(sectionName)) { for (int i = 0; i < section.lines.size(); ++i) { String line = section.lines.get(i); Matcher m = NAME_VAL_PATTERN.matcher(line); if (m.matches()) { String oldName = m.group(1); String oldValue = m.group(2); if (!line.startsWith(";") && oldName.equals(name) && (commentPattern == null || oldValue .matches(commentPattern))) { section.lines.set(i, ';' + line); } } } if (sectionName != null) { break; } } } } /** * Writes all changes to the INI configuration file * * @throws IOException */ public void close() throws IOException { flush(); } /** * Reads INI file contents * * @throws IOException */ protected void read() throws IOException { BufferedReader r = new BufferedReader(new FileReader(configFile)); String line; INIFileSection currentSection = new INIFileSection(GLOBAL_SECTION); sections.add(currentSection); while ((line = r.readLine()) != null) { line = line.trim(); Matcher sm = SECTION_PATTERN.matcher(line); if (sm.matches()) { String sectionName = sm.group(1); currentSection = new INIFileSection(sectionName); sections.add(currentSection); } else { Matcher nvm = NAME_VAL_PATTERN.matcher(line); // check only for double "include_path" (double include_path // problem) since other attributes like "extension" are allowed // more then once. if (nvm.matches() && "include_path".equals(nvm.group(1))) { removeEntry(nvm.group(1), null); } currentSection.lines.add(line); } } r.close(); } /** * Writes INI file contents back to the file * * @throws IOException */ protected void flush() throws IOException { PrintWriter w = new PrintWriter(new FileWriter(configFile)); for (INIFileSection section : sections) { if (section.name != GLOBAL_SECTION) { w.println('[' + section.name + ']'); } for (String line : section.lines) { w.println(line); } } w.close(); } /** * Get entry from the INI file from the global section. * * @param name * Entry name * @param removePattern * Pattern to check against existing entry value, before removing * it. If <code>removePattern</code> is <code>null</code> every * entry that matches the given name will be removed. * @return <code>true</code> if some entry was removed, otherwise - false */ public String getEntry(String name) { return getEntry(GLOBAL_SECTION, name); } /** * Get entry from the INI file. * * @param sectionName * Section name * @param name * Entry name */ public String getEntry(String sectionName, String name) { if (name == null) { throw new NullPointerException(); } for (INIFileSection section : sections) { for (int i = 0; i < section.lines.size(); ++i) { Matcher m = NAME_VAL_PATTERN.matcher(section.lines.get(i)); if (m.matches()) { String oldName = m.group(1); String oldValue = m.group(2); if (oldName.equals(name)) { return removeQuotes(oldValue); } } } } return null; } /** * Get all entries from the INI file from the global section. * * @param name * Entry name * @param removePattern * Pattern to check against existing entry value, before removing * it. If <code>removePattern</code> is <code>null</code> every * entry that matches the given name will be removed. * @return <code>true</code> if some entry was removed, otherwise - false */ public String[] getEntries(String name) { return getEntries(GLOBAL_SECTION, name); } /** * Get all entries from the INI file. * * @param sectionName * Section name * @param name * Entry name */ public String[] getEntries(String sectionName, String name) { if (name == null) { throw new NullPointerException(); } List<String> entries = new ArrayList<String>(); for (INIFileSection section : sections) { for (int i = 0; i < section.lines.size(); ++i) { Matcher m = NAME_VAL_PATTERN.matcher(section.lines.get(i)); if (m.matches()) { String oldName = m.group(1); String oldValue = m.group(2); if (oldName.equals(name)) { entries.add(removeQuotes(oldValue)); } } } } return entries.toArray(new String[0]); } }