/* * #%L * Common package for I/O and related utilities * %% * Copyright (C) 2005 - 2015 Open Microscopy Environment: * - Board of Regents of the University of Wisconsin-Madison * - Glencoe Software, Inc. * - University of Dundee * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * #L% */ package loci.common; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.InputStreamReader; import java.io.IOException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A simple parser for INI configuration files. Supports pound (#) as comments, * and backslash (\) to continue values across multiple lines. * * @author Curtis Rueden ctrueden at wisc.edu */ public class IniParser { // -- Fields -- /** Logger for this class. */ private static final Logger LOGGER = LoggerFactory.getLogger(IniParser.class); private String commentDelimiter = "#"; private boolean slashContinues = true; // -- IniParser API methods -- /** * Set the String that identifies a comment. Defaults to "#". */ public void setCommentDelimiter(String delimiter) { commentDelimiter = delimiter; } /** * Set whether or not a '\' at the end of a line signifies that the * line continues on the following line. * * By default, a '\' does continue the line. */ public void setBackslashContinuesLine(boolean slashContinues) { this.slashContinues = slashContinues; } /** Parses the INI-style configuration data from the given resource. */ public IniList parseINI(String path) throws IOException { return parseINI(openTextResource(path)); } /** * Parses the INI-style configuration data from the given resource, * using the given class to find the resource. */ public IniList parseINI(String path, Class<?> c) throws IOException { return parseINI(openTextResource(path, c)); } /** * Parses the INI-style wrapping the given file in a {@link BufferedReader} */ public IniList parseINI(File file) throws IOException { FileInputStream fis = null; InputStreamReader isr = null; BufferedReader br = null; try { fis = new FileInputStream(file); isr = new InputStreamReader(fis, Constants.ENCODING); br = new BufferedReader(isr); return parseINI(br); } finally { if (br != null) { br.close(); } if (isr != null) { isr.close(); } if (fis != null) { fis.close(); } } } /** Parses the INI-style configuration data from the given input stream. */ public IniList parseINI(BufferedReader in) throws IOException { IniList list = new IniList(); IniTable attrs = null; String chapter = null; int no = 1; StringBuffer sb = new StringBuffer(); while (true) { int num = readLine(in, sb); if (num == 0) break; // eof String line = sb.toString(); LOGGER.debug("Line {}: {}", no, line); // ignore blank lines if (line.equals("")) { no += num; continue; } // check for chapter header if (line.startsWith("{")) { // strip curly braces int end = line.length(); if (line.endsWith("}")) end--; chapter = line.substring(1, end); continue; } // check for section header if (line.startsWith("[")) { attrs = new IniTable(); list.add(attrs); // strip brackets int end = line.length(); if (line.endsWith("]")) end--; String header = line.substring(1, end); if (chapter != null) header = chapter + ": " + header; attrs.put(IniTable.HEADER_KEY, header); no += num; continue; } // if we still haven't found a header, then this is the default // section (more similar to a properties file) if (attrs == null) { attrs = new IniTable(); attrs.put(IniTable.HEADER_KEY, IniTable.DEFAULT_HEADER); list.add(attrs); } // parse key/value pair int equals = line.indexOf("="); if (equals < 0) throw new IOException(no + ": bad line"); String key = line.substring(0, equals).trim(); String value = line.substring(equals + 1).trim(); attrs.put(key, value); no += num; } return list; } // -- Utility methods -- /** Opens a buffered reader for the given resource. */ public static BufferedReader openTextResource(String path) { return openTextResource(path, IniParser.class); } /** Opens a buffered reader for the given resource. */ public static BufferedReader openTextResource(String path, Class<?> c) { try { return new BufferedReader(new InputStreamReader( c.getResourceAsStream(path), Constants.ENCODING)); } catch (IOException e) { LOGGER.error("Could not open BufferedReader", e); } return null; } // -- Helper methods -- /** * Reads (at least) one line from the given input stream * into the specified string buffer. * * @return number of lines read */ private int readLine(BufferedReader in, StringBuffer sb) throws IOException { int no = 0; sb.setLength(0); boolean blockText = false; while (true) { String line = in.readLine(); if (line == null) break; no++; // strip comments if (commentDelimiter != null) { int comment = line.indexOf(commentDelimiter); if (comment >= 0) line = line.substring(0, comment); } // kill whitespace if (!blockText) { line = line.trim(); } // backslash signifies data continues to next line boolean slash = slashContinues && line.trim().endsWith("\\"); blockText = slashContinues && line.trim().endsWith("\\n"); if (blockText) { line = line.substring(0, line.length() - 2) + "\n"; } else if (slash) { line = line.substring(0, line.length() - 1).trim() + " "; } sb.append(line); if (!slash && !blockText) break; } return no; } }