File Source: ConfigFile.java

         /* 
    P/P   *  Method: com.dmdirc.util.ConfigFile__static_init
          */
     1  /*
     2   * Copyright (c) 2006-2009 Chris Smith, Shane Mc Cormack, Gregory Holmes
     3   *
     4   * Permission is hereby granted, free of charge, to any person obtaining a copy
     5   * of this software and associated documentation files (the "Software"), to deal
     6   * in the Software without restriction, including without limitation the rights
     7   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     8   * copies of the Software, and to permit persons to whom the Software is
     9   * furnished to do so, subject to the following conditions:
    10   *
    11   * The above copyright notice and this permission notice shall be included in
    12   * all copies or substantial portions of the Software.
    13   *
    14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    20   * SOFTWARE.
    21   */
    22  
    23  package com.dmdirc.util;
    24  
    25  import java.io.File;
    26  import java.io.FileNotFoundException;
    27  import java.io.IOException;
    28  import java.io.InputStream;
    29  import java.nio.charset.Charset;
    30  import java.util.ArrayList;
    31  import java.util.GregorianCalendar;
    32  import java.util.HashMap;
    33  import java.util.List;
    34  import java.util.Map;
    35  
    36  /**
    37   * Reads and writes a standard DMDirc config file.
    38   *
    39   * @author chris
    40   */
    41  public class ConfigFile extends TextFile {
    42  
    43      /** A list of domains in this config file. */
    44      private final List<String> domains = new ArrayList<String>();
    45  
    46      /** The values associated with each flat domain. */
    47      private final MapList<String, String> flatdomains = new MapList<String, String>();
    48  
    49      /** The key/value sets associated with each key domain. */
    50      private final Map<String, Map<String, String>> keydomains
    51              = new HashMap<String, Map<String, String>>();
    52      
    53      /** Whether or not we should automatically create domains. */
    54      private boolean automake;
    55  
    56      /**
    57       * Creates a new read-only Config File from the specified input stream.
    58       * 
    59       * @param is The input stream to read
    60       */
    61      public ConfigFile(final InputStream is) {
                 /* 
    P/P           *  Method: void com.dmdirc.util.ConfigFile(InputStream)
                  * 
                  *  Postconditions:
                  *    init'ed(this.charset)
                  *    this.domains == &amp;new ArrayList(ConfigFile#1)
                  *    this.flatdomains == &amp;new MapList(ConfigFile#2)
                  *    this.is == is
                  *    init'ed(this.is)
                  *    this.keydomains == &amp;new HashMap(ConfigFile#3)
                  *    new ArrayList(ConfigFile#1) num objects == 1
                  *    new HashMap(ConfigFile#3) num objects == 1
                  *    new HashMap(MapList#1) num objects == 1
                  *    new MapList(ConfigFile#2) num objects == 1
                  *    ...
                  */
    62          super(is, Charset.forName("UTF-8"));
    63      }
    64  
    65      /**
    66       * Creates a new Config File from the specified file.
    67       * 
    68       * @param file The file to read/write
    69       */
    70      public ConfigFile(final File file) {
                 /* 
    P/P           *  Method: void com.dmdirc.util.ConfigFile(File)
                  * 
                  *  Postconditions:
                  *    init'ed(this.charset)
                  *    this.domains == &amp;new ArrayList(ConfigFile#1)
                  *    this.file == file
                  *    init'ed(this.file)
                  *    this.flatdomains == &amp;new MapList(ConfigFile#2)
                  *    this.keydomains == &amp;new HashMap(ConfigFile#3)
                  *    new ArrayList(ConfigFile#1) num objects == 1
                  *    new HashMap(ConfigFile#3) num objects == 1
                  *    new HashMap(MapList#1) num objects == 1
                  *    new MapList(ConfigFile#2) num objects == 1
                  *    ...
                  */
    71          super(file, Charset.forName("UTF-8"));
    72      }
    73  
    74      /**
    75       * Creates a new Config File from the specified file.
    76       * 
    77       * @param filename The name of the file to read/write
    78       */
    79      public ConfigFile(final String filename) {
                 /* 
    P/P           *  Method: void com.dmdirc.util.ConfigFile(String)
                  * 
                  *  Postconditions:
                  *    init'ed(this.charset)
                  *    this.domains == &amp;new ArrayList(ConfigFile#1)
                  *    this.file == &amp;new File(ConfigFile#1)
                  *    this.flatdomains == &amp;new MapList(ConfigFile#2)
                  *    this.keydomains == &amp;new HashMap(ConfigFile#3)
                  *    new ArrayList(ConfigFile#1) num objects == 1
                  *    new File(ConfigFile#1) num objects == 1
                  *    new HashMap(ConfigFile#3) num objects == 1
                  *    new HashMap(MapList#1) num objects == 1
                  *    new MapList(ConfigFile#2) num objects == 1
                  *    ...
                  */
    80          this(new File(filename));
    81      }
    82  
    83      /**
    84       * Sets the "automake" value of this config file. If automake is set to
    85       * true, any calls to getKeyDomain will automatically create the domain
    86       * if it did not previously exist.
    87       * 
    88       * @param automake The new value of the automake setting of this file
    89       */
    90      public void setAutomake(final boolean automake) {
                 /* 
    P/P           *  Method: void setAutomake(bool)
                  * 
                  *  Postconditions:
                  *    this.automake == automake
                  *    init'ed(this.automake)
                  */
    91          this.automake = automake;
    92      }
    93  
    94      /**
    95       * Reads the data from the file.
    96       * 
    97       * @throws FileNotFoundException if the file is not found
    98       * @throws IOException if an i/o exception occured when reading
    99       * @throws InvalidConfigFileException if the config file isn't valid
   100       */
   101      public void read() throws FileNotFoundException, IOException, InvalidConfigFileException {
                 /* 
    P/P           *  Method: void read()
                  * 
                  *  Preconditions:
                  *    this.domains != null
                  *    init'ed(this.file)
                  *    this.flatdomains != null
                  *    this.flatdomains.map != null
                  *    this.keydomains != null
                  *    (soft) init'ed(this.is)
                  * 
                  *  Presumptions:
                  *    java.util.Iterator:next(...)@112 != null
                  *    java.util.Map:get(...)@142 != null
                  * 
                  *  Postconditions:
                  *    this.lines == &amp;new ArrayList(readLines#4)
                  *    new ArrayList(readLines#4) num objects == 1
                  *    new ArrayList(readLines#4) num objects == 0
                  * 
                  *  Test Vectors:
                  *    java.lang.String:charAt(...)@115: {9}, {0..8, 10..216-1}
                  *    java.lang.String:charAt(...)@115: {0..31, 33..216-1}, {32}
                  *    java.lang.String:endsWith(...)@122: {0}, {1}
                  *    java.lang.String:endsWith(...)@122: {1}, {0}
                  *    java.lang.String:indexOf(...)@120: {0}, {-231..-1, 1..232-1}
                  *    java.lang.String:isEmpty(...)@115: {1}, {0}
                  *    java.lang.String:isEmpty(...)@120: {0}, {1}
                  *    java.util.Iterator:hasNext(...)@112: {0}, {1}
                  *    java.util.Map:containsKey(...)@132: {1}, {0}
                  *    java.util.Map:containsKey(...)@84: {1}, {0}
                  */
   102          String domain = null;
   103          boolean keydomain = false;
   104          int offset;
   105          
   106          keydomains.clear();
   107          flatdomains.clear();
   108          domains.clear();
   109          
   110          readLines();
   111  
   112          for (String line : getLines()) {
   113              String tline = line;
   114              
   115              while (!tline.isEmpty() && (tline.charAt(0) == '\t' || 
   116                      tline.charAt(0) == ' ')) {
   117                  tline = tline.substring(1);
   118              }
   119  
   120              if (tline.indexOf('#') == 0 || tline.isEmpty()) {
   121                  continue;
   122              } else if (
   123                      (tline.endsWith(":") && !tline.endsWith("\\:"))
   124                      && findEquals(tline) == -1) {
   125                  domain = unescape(tline.substring(0, tline.length() - 1));
   126  
   127                  domains.add(domain);
   128  
   129                  keydomain = keydomains.containsKey(domain)
   130                          || flatdomains.containsValue("keysections", domain);
   131                  
   132                  if (keydomain && !keydomains.containsKey(domain)) {
   133                      keydomains.put(domain, new HashMap<String, String>());
   134                  } else if (!keydomain && !flatdomains.containsKey(domain)) {
   135                      flatdomains.add(domain);
   136                  }
   137              } else if (domain != null && keydomain
   138                      && (offset = findEquals(tline)) != -1) {
   139                  final String key = unescape(tline.substring(0, offset));
   140                  final String value = unescape(tline.substring(offset + 1));
   141  
   142                  keydomains.get(domain).put(key, value);
   143              } else if (domain != null && !keydomain) {
   144                  flatdomains.add(domain, unescape(tline));
   145              } else {
   146                  throw new InvalidConfigFileException("Unknown or unexpected" +
   147                          " line encountered: " + tline);
   148              }
   149          }
   150      }
   151  
   152      /**
   153       * Writes the contents of this ConfigFile to disk.
   154       * 
   155       * @throws IOException if the write operation fails
   156       */
   157      public void write() throws IOException {
                 /* 
    P/P           *  Method: void write()
                  * 
                  *  Preconditions:
                  *    this.domains != null
                  *    this.file != null
                  *    (soft) this.flatdomains != null
                  *    (soft) this.flatdomains.map != null
                  *    (soft) this.keydomains != null
                  * 
                  *  Presumptions:
                  *    java.util.GregorianCalendar:getTime(...)@166 != null
                  *    java.util.Iterator:next(...)@170 != null
                  *    java.util.Iterator:next(...)@180 != null
                  *    java.util.Iterator:next(...)@184 != null
                  *    java.util.Map:entrySet(...)@184 != null
                  *    ...
                  * 
                  *  Test Vectors:
                  *    java.lang.String:equals(...)@171: {0}, {1}
                  *    java.util.Iterator:hasNext(...)@170: {0}, {1}
                  *    java.util.Iterator:hasNext(...)@184: {0}, {1}
                  *    java.util.Map:containsKey(...)@84: {0}, {1}
                  */
   158          if (!isWritable()) {
   159              throw new UnsupportedOperationException("Cannot write to a file "
   160                      + "that isn't writable");
   161          }
   162          
   163          final List<String> lines = new ArrayList<String>();
   164  
   165          lines.add("# This is a DMDirc configuration file.");
   166          lines.add("# Written on: " + new GregorianCalendar().getTime().toString());
   167  
   168          writeMeta(lines);
   169  
   170          for (String domain : domains) {
   171              if ("keysections".equals(domain)) {
   172                  continue;
   173              }
   174  
   175              lines.add("");
   176  
   177              lines.add(escape(domain) + ':');
   178  
   179              if (flatdomains.containsKey(domain)) {
   180                  for (String entry : flatdomains.get(domain)) {
   181                      lines.add("  " + escape(entry));
   182                  }
   183              } else {
   184                  for (Map.Entry<String, String> entry : keydomains.get(domain).entrySet()) {
   185                      lines.add("  " + escape(entry.getKey()) + "="
   186                              + escape(entry.getValue()));
   187                  }
   188              }
   189          }
   190  
   191          writeLines(lines);
   192      }
   193      
   194      /**
   195       * Appends the meta-data (keysections) to the specified list of lines.
   196       * 
   197       * @param lines The set of lines to be appended to
   198       */
   199      private void writeMeta(final List<String> lines) {
                 /* 
    P/P           *  Method: void writeMeta(List)
                  * 
                  *  Preconditions:
                  *    lines != null
                  *    this.domains != null
                  *    (soft) this.keydomains != null
                  * 
                  *  Test Vectors:
                  *    java.lang.String:equals(...)@207: {0}, {1}
                  *    java.util.Iterator:hasNext(...)@206: {0}, {1}
                  *    java.util.Map:containsKey(...)@209: {0}, {1}
                  */
   200          lines.add("");
   201          lines.add("# This section indicates which sections below take key/value");
   202          lines.add("# pairs, rather than a simple list. It should be placed above");
   203          lines.add("# any sections that take key/values.");
   204          lines.add("keysections:");
   205  
   206          for (String domain : domains) {
   207              if ("keysections".equals(domain)) {
   208                  continue;
   209              } else if (keydomains.containsKey(domain)) {
   210                  lines.add("  " + domain);
   211              }
   212          }
   213      }
   214      
   215      /**
   216       * Retrieves all the key domains for this config file.
   217       * 
   218       * @return This config file's key domains
   219       */
   220      public Map<String, Map<String, String>> getKeyDomains() {
                 /* 
    P/P           *  Method: Map getKeyDomains()
                  * 
                  *  Postconditions:
                  *    return_value == this.keydomains
                  *    init'ed(return_value)
                  */
   221          return keydomains;
   222      }
   223      
   224      /**
   225       * Retrieves the key/values of the specified key domain.
   226       * 
   227       * @param domain The domain to be retrieved
   228       * @return A map of keys to values in the specified domain
   229       */
   230      public Map<String, String> getKeyDomain(final String domain) {
                 /* 
    P/P           *  Method: Map getKeyDomain(String)
                  * 
                  *  Preconditions:
                  *    init'ed(this.automake)
                  *    this.keydomains != null
                  *    (soft) this.domains != null
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  * 
                  *  Test Vectors:
                  *    this.automake: {0}, {1}
                  *    java.util.Map:containsKey(...)@267: {1}, {0}
                  */
   231          if (automake && !isKeyDomain(domain)) {
   232              domains.add(domain);
   233              keydomains.put(domain, new HashMap<String, String>());
   234          }
   235          
   236          return keydomains.get(domain);
   237      }
   238      
   239      /**
   240       * Retrieves the content of the specified flat domain.
   241       * 
   242       * @param domain The domain to be retrieved
   243       * @return A list of lines in the specified domain
   244       */
   245      public List<String> getFlatDomain(final String domain) {
                 /* 
    P/P           *  Method: List getFlatDomain(String)
                  * 
                  *  Preconditions:
                  *    this.flatdomains != null
                  *    this.flatdomains.map != null
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  */
   246          return flatdomains.get(domain);
   247      }
   248      
   249      /**
   250       * Determines if this config file has the specified domain.
   251       * 
   252       * @param domain The domain to check for
   253       * @return True if the domain is known, false otherwise
   254       */
   255      public boolean hasDomain(final String domain) {
                 /* 
    P/P           *  Method: bool hasDomain(String)
                  * 
                  *  Preconditions:
                  *    this.keydomains != null
                  *    (soft) this.flatdomains != null
                  *    (soft) this.flatdomains.map != null
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  */
   256          return keydomains.containsKey(domain) || flatdomains.containsKey(domain);
   257      }
   258  
   259      /**
   260       * Determines if this config file has the specified domain, and the domain
   261       * is a key domain.
   262       * 
   263       * @param domain The domain to check for
   264       * @return True if the domain is known and keyed, false otherwise
   265       */
   266      public boolean isKeyDomain(final String domain) {
                 /* 
    P/P           *  Method: bool isKeyDomain(String)
                  * 
                  *  Preconditions:
                  *    this.keydomains != null
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  */
   267          return keydomains.containsKey(domain);
   268      }
   269  
   270      /**
   271       * Determines if this config file has the specified domain, and the domain
   272       * is a flat domain.
   273       * 
   274       * @param domain The domain to check for
   275       * @return True if the domain is known and flat, false otherwise
   276       */
   277      public boolean isFlatDomain(final String domain) {
                 /* 
    P/P           *  Method: bool isFlatDomain(String)
                  * 
                  *  Preconditions:
                  *    this.flatdomains != null
                  *    this.flatdomains.map != null
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  */
   278          return flatdomains.containsKey(domain);
   279      }
   280      
   281      /**
   282       * Adds a new flat domain to this config file.
   283       * 
   284       * @param name The name of the domain to be added
   285       * @param data The content of the domain
   286       */
   287      public void addDomain(final String name, final List<String> data) {
                 /* 
    P/P           *  Method: void addDomain(String, List)
                  * 
                  *  Preconditions:
                  *    this.domains != null
                  *    this.flatdomains != null
                  *    this.flatdomains.map != null
                  */
   288          domains.add(name);
   289          flatdomains.add(name, data);
   290      }
   291  
   292      /**
   293       * Adds a new key domain to this config file.
   294       * 
   295       * @param name The name of the domain to be added
   296       * @param data The content of the domain
   297       */    
   298      public void addDomain(final String name, final Map<String, String> data) {
                 /* 
    P/P           *  Method: void addDomain(String, Map)
                  * 
                  *  Preconditions:
                  *    this.domains != null
                  *    this.keydomains != null
                  */
   299          domains.add(name);
   300          keydomains.put(name, data);
   301      }
   302      
   303      /**
   304       * Unescapes any escaped characters in the specified input string.
   305       * 
   306       * @param input The string to unescape
   307       * @return The string with all escape chars (\) resolved
   308       */
   309      protected static String unescape(final String input) {
                 /* 
    P/P           *  Method: String unescape(String)
                  * 
                  *  Preconditions:
                  *    input != null
                  * 
                  *  Postconditions:
                  *    java.lang.StringBuilder:toString(...)._tainted == 0
                  *    return_value == &amp;java.lang.StringBuilder:toString(...)
                  * 
                  *  Test Vectors:
                  *    java.lang.String:charAt(...)@314: {110}, {92}, {114}
                  */
   310          boolean escaped = false;
   311          final StringBuilder temp = new StringBuilder();
   312  
   313          for (int i = 0; i < input.length(); i++) {
   314              final char ch = input.charAt(i);
   315  
   316              if (escaped) {
   317                  if (ch == 'n') {
   318                      temp.append('\n');
   319                  } else if (ch == 'r') {
   320                      temp.append('\r');
   321                  } else {
   322                      temp.append(ch);
   323                  }
   324                  
   325                  escaped = false;
   326              } else if (ch == '\\') {
   327                  escaped = true;
   328              } else {
   329                  temp.append(ch);
   330              }
   331          }
   332          
   333          return temp.toString();
   334      }
   335      
   336      /**
   337       * Escapes the specified input string by prefixing all occurances of
   338       * \, \n, \r, =, # and : with backslashes.
   339       * 
   340       * @param input The string to be escaped
   341       * @return A backslash-armoured version of the string
   342       */
   343      protected static String escape(final String input) {
                 /* 
    P/P           *  Method: String escape(String)
                  * 
                  *  Preconditions:
                  *    input != null
                  * 
                  *  Postconditions:
                  *    return_value != null
                  */
   344          return input.replaceAll("\\\\", "\\\\\\\\").replaceAll("\n", "\\\\n")
   345                  .replaceAll("\r", "\\\\r").replaceAll("=", "\\\\=")
   346                  .replaceAll(":", "\\\\:").replaceAll("#", "\\\\#");
   347      }
   348      
   349      /**
   350       * Finds the first non-escaped instance of '=' in the specified string.
   351       * 
   352       * @param input The string to be searched
   353       * @return The offset of the first non-escaped instance of '=', or -1.
   354       */
   355      protected static int findEquals(final String input) {
                 /* 
    P/P           *  Method: int findEquals(String)
                  * 
                  *  Preconditions:
                  *    input != null
                  * 
                  *  Postconditions:
                  *    return_value in {-1..232-2}
                  * 
                  *  Test Vectors:
                  *    java.lang.String:charAt(...)@361: {0..91, 93..216-1}, {92}
                  *    java.lang.String:charAt(...)@363: {0..60, 62..216-1}, {61}
                  */
   356          boolean escaped = false;
   357          
   358          for (int i = 0; i < input.length(); i++) {
   359              if (escaped) {
   360                  escaped = false;
   361              } else if (input.charAt(i) == '\\') {
   362                  escaped = true;
   363              } else if (input.charAt(i) == '=') {
   364                  return i;
   365              }
   366          }
   367          
   368          return -1;
   369      }
   370  }








SofCheck Inspector Build Version : 2.17854
ConfigFile.java 2009-Jun-25 01:54:24
ConfigFile.class 2009-Sep-02 17:04:17