1 /***********************************************************************
2  * Copyright (c) 2013-2024 Commonwealth Computer Research, Inc.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Apache License, Version 2.0
5  * which accompanies this distribution and is available at
6  * http://www.opensource.org/licenses/apache2.0.php.
7  ***********************************************************************/
8 
9 package org.locationtech.geomesa.convert
10 
11 import com.typesafe.config.Config
12 import org.apache.commons.csv.{CSVFormat, CSVParser}
13 import org.locationtech.geomesa.utils.classpath.ServiceLoader
14 import org.locationtech.geomesa.utils.io.WithClose
15 
16 import java.io.{Closeable, InputStreamReader}
17 import java.nio.charset.StandardCharsets
18 
19 trait EnrichmentCache extends Closeable {
20   def get(args: Array[String]): Any
21   def put(args: Array[String], value: Any): Unit
22   def clear(): Unit
23 }
24 
25 trait EnrichmentCacheFactory {
26   def canProcess(conf: Config): Boolean
27   def build(conf: Config): EnrichmentCache
28 }
29 
30 object EnrichmentCache {
31 
32   lazy private val factories = ServiceLoader.load[EnrichmentCacheFactory]()
33 
34   def apply(conf: Config): EnrichmentCache = {
35     factories.find(_.canProcess(conf))
36         .getOrElse(throw new RuntimeException(s"Could not find applicable EnrichmentCache for config: $conf"))
37         .build(conf)
38   }
39 }
40 
41 // For testing purposes
42 class SimpleEnrichmentCache(val cache: java.util.Map[String, java.util.HashMap[String, AnyRef]] = new java.util.HashMap[String, java.util.HashMap[String, AnyRef]]())
43     extends EnrichmentCache {
44 
45   import scala.collection.JavaConverters._
46 
47   override def get(args: Array[String]): Any = Option(cache.get(args(0))).map(_.get(args(1))).orNull
48 
49   override def put(args: Array[String], value: Any): Unit =
50     cache.asScala.getOrElseUpdate(args(0), new java.util.HashMap[String, AnyRef]()).put(args(1), value.asInstanceOf[AnyRef])
51 
52   override def clear(): Unit = cache.clear()
53 
54   override def close(): Unit = {}
55 }
56 
57 class SimpleEnrichmentCacheFactory extends EnrichmentCacheFactory {
58   override def canProcess(conf: Config): Boolean = conf.hasPath("type") && conf.getString("type").equals("simple")
59 
60   override def build(conf: Config): EnrichmentCache = new SimpleEnrichmentCache(conf.getConfig("data").root().unwrapped().asInstanceOf[java.util.Map[String, java.util.HashMap[String, AnyRef]]])
61 }
62 
63 class ResourceLoadingCache(path: String, idField: String, headers: Seq[String]) extends EnrichmentCache {
64   import scala.collection.JavaConverters._
65 
66   private val data = {
67     val loader = Option(Thread.currentThread().getContextClassLoader).getOrElse(getClass.getClassLoader)
68     val stream = loader.getResourceAsStream(path)
69     if (stream == null) {
70       throw new IllegalArgumentException(s"Could not load the resources at '$path'")
71     }
72     val reader = new InputStreamReader(stream, StandardCharsets.UTF_8)
73     val format = CSVFormat.DEFAULT.withHeader(headers: _*)
74     WithClose(new CSVParser(reader, format)) { reader =>
75       reader.getRecords.asScala.map(rec => rec.get(idField) -> rec.toMap).toMap
76     }
77   }
78 
79   override def get(args: Array[String]): Any = data.get(args(0)).map(_.get(args(1))).orNull
80   override def put(args: Array[String], value: Any): Unit = ???
81   override def clear(): Unit = ???
82   override def close(): Unit = {}
83 }
84 
85 class ResourceLoadingCacheFactory extends EnrichmentCacheFactory {
86   override def canProcess(conf: Config): Boolean = conf.hasPath("type") && conf.getString("type").equals("resource")
87 
88   override def build(conf: Config): EnrichmentCache = {
89     import scala.collection.JavaConverters._
90 
91     val path = conf.getString("path")
92     val idField = conf.getString("id-field")
93     val headers = conf.getStringList("columns")
94     new ResourceLoadingCache(path, idField, headers.asScala.toList)
95   }
96 }
Line Stmt Id Pos Tree Symbol Tests Code
37 160 1217 - 1383 Apply org.locationtech.geomesa.convert.EnrichmentCacheFactory.build EnrichmentCache.this.factories.find(((x$1: org.locationtech.geomesa.convert.EnrichmentCacheFactory) => x$1.canProcess(conf))).getOrElse[org.locationtech.geomesa.convert.EnrichmentCacheFactory](throw new scala.`package`.RuntimeException(scala.StringContext.apply("Could not find applicable EnrichmentCache for config: ", "").s(conf))).build(conf)
47 161 1720 - 1727 Apply scala.Array.apply args.apply(0)
47 162 1710 - 1728 Apply java.util.Map.get SimpleEnrichmentCache.this.cache.get(args.apply(0))
47 163 1740 - 1747 Apply scala.Array.apply args.apply(1)
47 164 1734 - 1748 Apply java.util.HashMap.get x$2.get(args.apply(1))
47 165 1750 - 1750 TypeApply scala.Predef.$conforms scala.Predef.$conforms[Null]
47 166 1703 - 1756 ApplyToImplicitArgs scala.Option.orNull scala.Option.apply[java.util.HashMap[String,AnyRef]](SimpleEnrichmentCache.this.cache.get(args.apply(0))).map[AnyRef](((x$2: java.util.HashMap[String,AnyRef]) => x$2.get(args.apply(1)))).orNull[Any](scala.Predef.$conforms[Null])
50 167 1822 - 1827 Select org.locationtech.geomesa.convert.SimpleEnrichmentCache.cache SimpleEnrichmentCache.this.cache
50 168 1852 - 1859 Apply scala.Array.apply args.apply(0)
50 169 1861 - 1900 Apply java.util.HashMap.<init> new java.util.HashMap[String,AnyRef]()
50 170 1906 - 1913 Apply scala.Array.apply args.apply(1)
50 171 1915 - 1941 TypeApply scala.Any.asInstanceOf value.asInstanceOf[AnyRef]
50 172 1822 - 1942 Apply java.util.HashMap.put scala.collection.JavaConverters.mapAsScalaMapConverter[String, java.util.HashMap[String,AnyRef]](SimpleEnrichmentCache.this.cache).asScala.getOrElseUpdate(args.apply(0), new java.util.HashMap[String,AnyRef]()).put(args.apply(1), value.asInstanceOf[AnyRef])
50 173 1905 - 1905 Literal <nosymbol> ()
52 174 1975 - 1988 Apply java.util.Map.clear SimpleEnrichmentCache.this.cache.clear()
54 175 2021 - 2023 Literal <nosymbol> ()
58 176 2159 - 2165 Literal <nosymbol> "type"
58 177 2170 - 2209 Apply java.lang.String.equals conf.getString("type").equals("simple")
58 178 2146 - 2209 Apply scala.Boolean.&& conf.hasPath("type").&&(conf.getString("type").equals("simple"))
60 179 2306 - 2312 Literal <nosymbol> "data"
60 180 2291 - 2403 TypeApply scala.Any.asInstanceOf conf.getConfig("data").root().unwrapped().asInstanceOf[java.util.Map[String,java.util.HashMap[String,AnyRef]]]
60 181 2265 - 2404 Apply org.locationtech.geomesa.convert.SimpleEnrichmentCache.<init> new SimpleEnrichmentCache(conf.getConfig("data").root().unwrapped().asInstanceOf[java.util.Map[String,java.util.HashMap[String,AnyRef]]])
67 182 2605 - 2649 Apply java.lang.Thread.getContextClassLoader java.lang.Thread.currentThread().getContextClassLoader()
67 183 2661 - 2684 Apply java.lang.Class.getClassLoader ResourceLoadingCache.this.getClass().getClassLoader()
67 184 2598 - 2685 Apply scala.Option.getOrElse scala.Option.apply[ClassLoader](java.lang.Thread.currentThread().getContextClassLoader()).getOrElse[ClassLoader](ResourceLoadingCache.this.getClass().getClassLoader())
68 185 2730 - 2734 Select org.locationtech.geomesa.convert.ResourceLoadingCache.path ResourceLoadingCache.this.path
68 186 2703 - 2735 Apply java.lang.ClassLoader.getResourceAsStream loader.getResourceAsStream(ResourceLoadingCache.this.path)
69 187 2744 - 2758 Apply java.lang.Object.== stream.==(null)
69 190 2740 - 2740 Literal <nosymbol> ()
69 191 2740 - 2740 Block <nosymbol> ()
70 188 2768 - 2846 Throw <nosymbol> throw new scala.`package`.IllegalArgumentException(scala.StringContext.apply("Could not load the resources at \'", "\'").s(ResourceLoadingCache.this.path))
70 189 2768 - 2846 Block <nosymbol> throw new scala.`package`.IllegalArgumentException(scala.StringContext.apply("Could not load the resources at \'", "\'").s(ResourceLoadingCache.this.path))
72 192 2900 - 2922 Select java.nio.charset.StandardCharsets.UTF_8 java.nio.charset.StandardCharsets.UTF_8
72 193 2870 - 2923 Apply java.io.InputStreamReader.<init> new java.io.InputStreamReader(stream, java.nio.charset.StandardCharsets.UTF_8)
73 194 2970 - 2977 Select org.locationtech.geomesa.convert.ResourceLoadingCache.headers ResourceLoadingCache.this.headers
73 195 2941 - 2982 Apply org.apache.commons.csv.CSVFormat.withHeader org.apache.commons.csv.CSVFormat.DEFAULT.withHeader((ResourceLoadingCache.this.headers: _*))
74 196 2997 - 3026 Apply org.apache.commons.csv.CSVParser.<init> new org.apache.commons.csv.CSVParser(reader, format)
74 205 3028 - 3028 Select org.locationtech.geomesa.utils.io.IsCloseableImplicits.closeableIsCloseable io.this.IsCloseable.closeableIsCloseable
74 206 2987 - 3125 ApplyToImplicitArgs org.locationtech.geomesa.utils.io.WithClose.apply org.locationtech.geomesa.utils.io.`package`.WithClose.apply[org.apache.commons.csv.CSVParser, scala.collection.immutable.Map[String,java.util.Map[String,String]]](new org.apache.commons.csv.CSVParser(reader, format))(((reader: org.apache.commons.csv.CSVParser) => scala.collection.JavaConverters.asScalaBufferConverter[org.apache.commons.csv.CSVRecord](reader.getRecords()).asScala.map[(String, java.util.Map[String,String]), scala.collection.mutable.Buffer[(String, java.util.Map[String,String])]](((rec: org.apache.commons.csv.CSVRecord) => scala.Predef.ArrowAssoc[String](rec.get(ResourceLoadingCache.this.idField)).->[java.util.Map[String,String]](rec.toMap())))(mutable.this.Buffer.canBuildFrom[(String, java.util.Map[String,String])]).toMap[String, java.util.Map[String,String]](scala.Predef.$conforms[(String, java.util.Map[String,String])])))(io.this.IsCloseable.closeableIsCloseable)
75 197 3046 - 3063 Apply org.apache.commons.csv.CSVParser.getRecords reader.getRecords()
75 198 3091 - 3098 Select org.locationtech.geomesa.convert.ResourceLoadingCache.idField ResourceLoadingCache.this.idField
75 199 3083 - 3099 Apply org.apache.commons.csv.CSVRecord.get rec.get(ResourceLoadingCache.this.idField)
75 200 3103 - 3112 Apply org.apache.commons.csv.CSVRecord.toMap rec.toMap()
75 201 3083 - 3112 Apply scala.Predef.ArrowAssoc.-> scala.Predef.ArrowAssoc[String](rec.get(ResourceLoadingCache.this.idField)).->[java.util.Map[String,String]](rec.toMap())
75 202 3075 - 3075 TypeApply scala.collection.mutable.Buffer.canBuildFrom mutable.this.Buffer.canBuildFrom[(String, java.util.Map[String,String])]
75 203 3114 - 3114 TypeApply scala.Predef.$conforms scala.Predef.$conforms[(String, java.util.Map[String,String])]
75 204 3046 - 3119 ApplyToImplicitArgs scala.collection.TraversableOnce.toMap scala.collection.JavaConverters.asScalaBufferConverter[org.apache.commons.csv.CSVRecord](reader.getRecords()).asScala.map[(String, java.util.Map[String,String]), scala.collection.mutable.Buffer[(String, java.util.Map[String,String])]](((rec: org.apache.commons.csv.CSVRecord) => scala.Predef.ArrowAssoc[String](rec.get(ResourceLoadingCache.this.idField)).->[java.util.Map[String,String]](rec.toMap())))(mutable.this.Buffer.canBuildFrom[(String, java.util.Map[String,String])]).toMap[String, java.util.Map[String,String]](scala.Predef.$conforms[(String, java.util.Map[String,String])])
79 207 3187 - 3194 Apply scala.Array.apply args.apply(0)
79 208 3206 - 3213 Apply scala.Array.apply args.apply(1)
79 209 3200 - 3214 Apply java.util.Map.get x$3.get(args.apply(1))
79 210 3216 - 3216 TypeApply scala.Predef.$conforms scala.Predef.$conforms[Null]
79 211 3178 - 3222 ApplyToImplicitArgs scala.Option.orNull ResourceLoadingCache.this.data.get(args.apply(0)).map[String](((x$3: java.util.Map[String,String]) => x$3.get(args.apply(1)))).orNull[Any](scala.Predef.$conforms[Null])
80 212 3283 - 3286 Select scala.Predef.??? scala.Predef.???
81 213 3318 - 3321 Select scala.Predef.??? scala.Predef.???
82 214 3353 - 3355 Literal <nosymbol> ()
86 215 3490 - 3496 Literal <nosymbol> "type"
86 216 3501 - 3542 Apply java.lang.String.equals conf.getString("type").equals("resource")
86 217 3477 - 3542 Apply scala.Boolean.&& conf.hasPath("type").&&(conf.getString("type").equals("resource"))
91 218 3661 - 3683 Apply com.typesafe.config.Config.getString conf.getString("path")
92 219 3702 - 3728 Apply com.typesafe.config.Config.getString conf.getString("id-field")
93 220 3747 - 3776 Apply com.typesafe.config.Config.getStringList conf.getStringList("columns")
94 221 3821 - 3843 Select scala.collection.TraversableOnce.toList scala.collection.JavaConverters.asScalaBufferConverter[String](headers).asScala.toList
94 222 3781 - 3844 Apply org.locationtech.geomesa.convert.ResourceLoadingCache.<init> new ResourceLoadingCache(path, idField, scala.collection.JavaConverters.asScalaBufferConverter[String](headers).asScala.toList)