Config.scala 7.76 KB
Newer Older
Peter van 't Hof's avatar
Peter van 't Hof committed
1
2
3
4
package nl.lumc.sasc.biopet.core.config

import nl.lumc.sasc.biopet.core._
import java.io.File
Peter van 't Hof's avatar
Peter van 't Hof committed
5
import org.broadinstitute.gatk.queue.util.Logging
Peter van 't Hof's avatar
Peter van 't Hof committed
6
7
import argonaut._, Argonaut._
import scalaz._, Scalaz._
Peter van 't Hof's avatar
Peter van 't Hof committed
8
9

class Config(var map: Map[String,Any]) extends Logging {
10
  logger.debug("Init phase of config")
Peter van 't Hof's avatar
Peter van 't Hof committed
11
12
13
14
15
16
17
18
19
20
21
22
  def this() = {
    this(Map())
    loadDefaultConfig()
  }
  
  def loadDefaultConfig() {
    var globalFile: String = System.getenv("BIOPET_CONFIG")
    if (globalFile != null) {
      var file: File = new File(globalFile)
      if (file.exists()) {
        logger.info("Loading config file: " + file)
        loadConfigFile(file)
Peter van 't Hof's avatar
Peter van 't Hof committed
23
      } else logger.warn("BIOPET_CONFIG value found but file does not exist, no global config is loaded")
Peter van 't Hof's avatar
Peter van 't Hof committed
24
25
26
27
    } else logger.info("BIOPET_CONFIG value not found, no global config is loaded")
  }
  
  def loadConfigFile(configFile:File) {
Peter van 't Hof's avatar
Peter van 't Hof committed
28
29
30
31
32
33
    logger.debug("Jsonfile: " + configFile)
    val jsonText = scala.io.Source.fromFile(configFile).mkString
    val json = Parse.parseOption(jsonText)
    logger.debug(json)
    val configJson = jsonToMap(json.get)
    logger.debug("Contain: " + configJson)
Peter van 't Hof's avatar
Peter van 't Hof committed
34
35
36
37
    if (configJson == None) {
      throw new IllegalStateException("The config JSON file is either not properly formatted or not a JSON file, file: " + configFile)
    }
    
Peter van 't Hof's avatar
Peter van 't Hof committed
38
39
40
41
42
43
44
45
46
47
48
    if (map.isEmpty) map = configJson
    else map = Config.mergeMaps(configJson, map)
    logger.debug("New config: " + map)
  }
  
  private def jsonToMap(json:Json) : Map[String, Any] = {
    var output: Map[String, Any] = Map()
    if (json.isObject) {
      for (key <- json.objectFieldsOrEmpty) {
        val value: Any = jsonToAny(json.field(key).get)
        output += (key -> value)
Peter van 't Hof's avatar
Peter van 't Hof committed
49
      }
Peter van 't Hof's avatar
Peter van 't Hof committed
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
    } else return null
    return output
  }
  
  private def jsonToAny(json:Json): Any = {
    if (json.isObject) return jsonToMap(json)
    else if (json.isArray) {
     var list:List[Any] = List()
     for (value <- json.objectValues.get) list ::= jsonToAny(value)
     return list
    } else if (json.isBool) return json.bool.get
    else if (json.isString) return json.string.get.toString
    else if (json.isNumber) {
      val num = json.number.get
      if (num.toString.contains(".")) return num.toDouble
      else return num.toLong
    } else throw new IllegalStateException("Config value type not supported, value: " + json)
Peter van 't Hof's avatar
Peter van 't Hof committed
67
68
69
70
71
72
  }
  
  def getMap() : Map[String,Any] = map
  
  var notFoundCache: List[ConfigValueIndex] = List()
  var foundCache: Map[ConfigValueIndex,ConfigValue] = Map()
Peter van 't Hof's avatar
Peter van 't Hof committed
73
  var defaultCache: Map[ConfigValueIndex,ConfigValue] = Map()
Peter van 't Hof's avatar
Peter van 't Hof committed
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
  
  def contains(s:String) : Boolean = map.contains(s)
  def contains(requestedIndex:ConfigValueIndex) : Boolean = contains(requestedIndex.module, requestedIndex.path, requestedIndex.key)
  def contains(module:String, path: List[String], key:String) : Boolean = {
    val requestedIndex = ConfigValueIndex(module,path,key)
    if (notFoundCache.contains(requestedIndex)) return false
    else if (foundCache.contains(requestedIndex)) return true
    else {
      var submodules = path.reverse
      while (!submodules.isEmpty) {
        var submodules2 = submodules
        while (!submodules2.isEmpty) {
          val p = getMapFromPath(submodules2 ::: module :: Nil)
          //logger.debug("p: " + p)
          if (p.contains(key)) {
            foundCache += (requestedIndex -> ConfigValue.apply(requestedIndex, ConfigValueIndex(module,submodules2,key), p(key)))
            return true
          }

          val p2 = getMapFromPath(submodules2)
          //logger.debug("p2: " + p2)
          if (p2.contains(key)) {
            foundCache += (requestedIndex -> ConfigValue.apply(requestedIndex, ConfigValueIndex(module,submodules2,key), p2(key)))
            return true
          }
          submodules2 = submodules2.init
        }
        submodules = submodules.tail
      }
      val p = getMapFromPath(module :: Nil)
      if (p.contains(key)) { // Module is not nested
        foundCache += (requestedIndex -> ConfigValue.apply(requestedIndex, ConfigValueIndex(module,Nil,key), p(key)))
        return true
      } else if (this.contains(key)) { // Root value of json
        foundCache += (requestedIndex -> ConfigValue.apply(requestedIndex, ConfigValueIndex("",Nil,key), get(key)))
        return true
      } else { // At this point key is not found on the path
        notFoundCache +:= requestedIndex
        return false
      }
    }
  }
  
  private def get(key:String) : Any = map(key)
  private def get(key:String, default:Any) : Any = if (contains(key)) get(key) else default
  
  def apply(module:String, path: List[String], key:String, default:Any) : ConfigValue = {
    val requestedIndex = ConfigValueIndex(module,path,key)
    if (contains(requestedIndex)) return foundCache(requestedIndex)
    else {
Peter van 't Hof's avatar
Peter van 't Hof committed
124
125
      defaultCache += (requestedIndex -> ConfigValue.apply(requestedIndex, null, default, true))
      return defaultCache(requestedIndex)
Peter van 't Hof's avatar
Peter van 't Hof committed
126
127
128
129
130
131
132
    }
  }
  
  def apply(module:String, path: List[String], key:String) : ConfigValue = {
    val requestedIndex = ConfigValueIndex(module,path,key)
    if (contains(requestedIndex)) return foundCache(requestedIndex)
    else {
Peter van 't Hof's avatar
Peter van 't Hof committed
133
      logger.error("Value in config could not be found but it seems required, index: " + requestedIndex)
Peter van 't Hof's avatar
Peter van 't Hof committed
134
135
136
137
138
139
140
141
142
143
144
145
146
      throw new IllegalStateException("Value in config could not be found but it seems required, index: " + requestedIndex)
    }
  }
  
  private def getMapFromPath(path: List[String]) : Map[String,Any] = {
    var returnMap: Map[String,Any] = map
    for (m <- path) {
      if (!returnMap.contains(m)) return Map()
      else returnMap = Config.valueToMap(returnMap(m))
    }
    return returnMap
  }
  
Peter van 't Hof's avatar
Peter van 't Hof committed
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
  def getReport: String = {
    var output:StringBuilder = new StringBuilder
    output.append("Config report, sorted on module:\n")
    var modules:Map[String,StringBuilder] = Map()
    for ((key,value) <- foundCache) {
      val module = key.module
      if (!modules.contains(module)) modules += (module -> new StringBuilder)
      modules(module).append("Found: " + value.toString + "\n")
    }
    for ((key,value) <- defaultCache) {
      val module = key.module
      if (!modules.contains(module)) modules += (module -> new StringBuilder)
      modules(module).append("Default used: " + value.toString + "\n")
    }
    for (value <- notFoundCache) {
      val module = value.module
      if (!modules.contains(module)) modules += (module -> new StringBuilder)
      if (!defaultCache.contains(value)) modules(module).append("Not Found: " + value.toString + "\n")
    }
    for ((key,value) <- modules) {
      output.append("Config options for module: " + key + "\n")
      output.append(value.toString)
      output.append("\n")
    }
    return output.toString
Peter van 't Hof's avatar
Peter van 't Hof committed
172
  }
Peter van 't Hof's avatar
Peter van 't Hof committed
173
      
Peter van 't Hof's avatar
Peter van 't Hof committed
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
  override def toString() : String = map.toString
}

object Config {  
  def valueToMap(input:Any) : Map[String,Any] = {
    input match {
      case m:Map[_, _] => return m.asInstanceOf[Map[String,Any]]
      case _ => throw new IllegalStateException("Value '" + input + "' is not an Map")
    }
  }
  
  def mergeMaps(map1:Map[String,Any],map2:Map[String,Any]) : Map[String,Any] = {
    var newMap: Map[String,Any] = Map()
    for (key <- map1.keySet.++(map2.keySet)) {
      if (map1.contains(key) && !map2.contains(key)) newMap += (key -> map1(key))
      else if (!map1.contains(key) && map2.contains(key)) newMap += (key -> map2(key))
      else if (map1.contains(key) && map2.contains(key)) {
        map1(key) match { 
          case m1:Map[_,_] => {
            map2(key) match {
              case m2:Map[_,_] => newMap += (key -> mergeMaps(Config.valueToMap(m1),Config.valueToMap(m2)))
              case _ => newMap += (key -> map1(key))
            }
          }
          case _ => newMap += (key -> map1(key))
        }
      }
    }
    return newMap
  }
  
  def mergeConfigs(config1:Config,config2:Config) : Config = new Config(mergeMaps(config1.getMap, config2.getMap))
}