1# This Source Code Form is subject to the terms of the Mozilla Public
2# License, v. 2.0. If a copy of the MPL was not distributed with this file,
3# You can obtain one at http://mozilla.org/MPL/2.0/.
4
5# This file contains utility functions for reading .properties files, like
6# region.properties.
7
8from __future__ import absolute_import, print_function, unicode_literals
9
10import codecs
11import re
12import six
13import sys
14
15if sys.version_info[0] == 3:
16    str_type = str
17else:
18    str_type = basestring
19
20
21class DotProperties:
22    r"""A thin representation of a key=value .properties file."""
23
24    def __init__(self, file=None):
25        self._properties = {}
26        if file:
27            self.update(file)
28
29    def update(self, file):
30        """Updates properties from a file name or file-like object.
31
32        Ignores empty lines and comment lines."""
33
34        if isinstance(file, str_type):
35            f = codecs.open(file, "r", "utf-8")
36        else:
37            f = file
38
39        for l in f.readlines():
40            line = l.strip()
41            if not line or line.startswith("#"):
42                continue
43            (k, v) = re.split("\s*=\s*", line, 1)
44            self._properties[k] = v
45
46    def get(self, key, default=None):
47        return self._properties.get(key, default)
48
49    def get_list(self, prefix):
50        """Turns {'list.0':'foo', 'list.1':'bar'} into ['foo', 'bar'].
51
52        Returns [] to indicate an empty or missing list."""
53
54        if not prefix.endswith("."):
55            prefix = prefix + "."
56        indexes = []
57        for k, v in six.iteritems(self._properties):
58            if not k.startswith(prefix):
59                continue
60            key = k[len(prefix) :]
61            if "." in key:
62                # We have something like list.sublist.0.
63                continue
64            indexes.append(int(key))
65        return [self._properties[prefix + str(index)] for index in sorted(indexes)]
66
67    def get_dict(self, prefix, required_keys=[]):
68        """Turns {'foo.title':'title', ...} into {'title':'title', ...}.
69
70        If ``|required_keys|`` is present, it must be an iterable of required key
71        names.  If a required key is not present, ValueError is thrown.
72
73        Returns {} to indicate an empty or missing dict."""
74
75        if not prefix.endswith("."):
76            prefix = prefix + "."
77
78        D = dict(
79            (k[len(prefix) :], v)
80            for k, v in six.iteritems(self._properties)
81            if k.startswith(prefix) and "." not in k[len(prefix) :]
82        )
83
84        for required_key in required_keys:
85            if required_key not in D:
86                raise ValueError("Required key %s not present" % required_key)
87
88        return D
89