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 53398 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 53399 1720 - 1727 Apply scala.Array.apply args.apply(0)
47 53400 1710 - 1728 Apply java.util.Map.get SimpleEnrichmentCache.this.cache.get(args.apply(0))
47 53401 1740 - 1747 Apply scala.Array.apply args.apply(1)
47 53402 1734 - 1748 Apply java.util.HashMap.get x$2.get(args.apply(1))
47 53403 1750 - 1750 TypeApply scala.Predef.$conforms scala.Predef.$conforms[Null]
47 53404 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 53405 1822 - 1827 Select org.locationtech.geomesa.convert.SimpleEnrichmentCache.cache SimpleEnrichmentCache.this.cache
50 53406 1852 - 1859 Apply scala.Array.apply args.apply(0)
50 53407 1861 - 1900 Apply java.util.HashMap.<init> new java.util.HashMap[String,AnyRef]()
50 53408 1906 - 1913 Apply scala.Array.apply args.apply(1)
50 53409 1915 - 1941 TypeApply scala.Any.asInstanceOf value.asInstanceOf[AnyRef]
50 53410 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 53411 1905 - 1905 Literal <nosymbol> ()
52 53412 1975 - 1988 Apply java.util.Map.clear SimpleEnrichmentCache.this.cache.clear()
54 53413 2021 - 2023 Literal <nosymbol> ()
58 53414 2159 - 2165 Literal <nosymbol> "type"
58 53415 2170 - 2209 Apply java.lang.String.equals conf.getString("type").equals("simple")
58 53416 2146 - 2209 Apply scala.Boolean.&& conf.hasPath("type").&&(conf.getString("type").equals("simple"))
60 53417 2306 - 2312 Literal <nosymbol> "data"
60 53418 2291 - 2403 TypeApply scala.Any.asInstanceOf conf.getConfig("data").root().unwrapped().asInstanceOf[java.util.Map[String,java.util.HashMap[String,AnyRef]]]
60 53419 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 53420 2605 - 2649 Apply java.lang.Thread.getContextClassLoader java.lang.Thread.currentThread().getContextClassLoader()
67 53421 2661 - 2684 Apply java.lang.Class.getClassLoader ResourceLoadingCache.this.getClass().getClassLoader()
67 53422 2598 - 2685 Apply scala.Option.getOrElse scala.Option.apply[ClassLoader](java.lang.Thread.currentThread().getContextClassLoader()).getOrElse[ClassLoader](ResourceLoadingCache.this.getClass().getClassLoader())
68 53423 2730 - 2734 Select org.locationtech.geomesa.convert.ResourceLoadingCache.path ResourceLoadingCache.this.path
68 53424 2703 - 2735 Apply java.lang.ClassLoader.getResourceAsStream loader.getResourceAsStream(ResourceLoadingCache.this.path)
69 53425 2744 - 2758 Apply java.lang.Object.== stream.==(null)
69 53428 2740 - 2740 Literal <nosymbol> ()
69 53429 2740 - 2740 Block <nosymbol> ()
70 53426 2768 - 2846 Throw <nosymbol> throw new scala.`package`.IllegalArgumentException(scala.StringContext.apply("Could not load the resources at \'", "\'").s(ResourceLoadingCache.this.path))
70 53427 2768 - 2846 Block <nosymbol> throw new scala.`package`.IllegalArgumentException(scala.StringContext.apply("Could not load the resources at \'", "\'").s(ResourceLoadingCache.this.path))
72 53430 2900 - 2922 Select java.nio.charset.StandardCharsets.UTF_8 java.nio.charset.StandardCharsets.UTF_8
72 53431 2870 - 2923 Apply java.io.InputStreamReader.<init> new java.io.InputStreamReader(stream, java.nio.charset.StandardCharsets.UTF_8)
73 53432 2970 - 2977 Select org.locationtech.geomesa.convert.ResourceLoadingCache.headers ResourceLoadingCache.this.headers
73 53433 2941 - 2982 Apply org.apache.commons.csv.CSVFormat.withHeader org.apache.commons.csv.CSVFormat.DEFAULT.withHeader((ResourceLoadingCache.this.headers: _*))
74 53434 2997 - 3026 Apply org.apache.commons.csv.CSVParser.<init> new org.apache.commons.csv.CSVParser(reader, format)
74 53443 3028 - 3028 Select org.locationtech.geomesa.utils.io.IsCloseableImplicits.closeableIsCloseable io.this.IsCloseable.closeableIsCloseable
74 53444 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 53435 3046 - 3063 Apply org.apache.commons.csv.CSVParser.getRecords reader.getRecords()
75 53436 3091 - 3098 Select org.locationtech.geomesa.convert.ResourceLoadingCache.idField ResourceLoadingCache.this.idField
75 53437 3083 - 3099 Apply org.apache.commons.csv.CSVRecord.get rec.get(ResourceLoadingCache.this.idField)
75 53438 3103 - 3112 Apply org.apache.commons.csv.CSVRecord.toMap rec.toMap()
75 53439 3083 - 3112 Apply scala.Predef.ArrowAssoc.-> scala.Predef.ArrowAssoc[String](rec.get(ResourceLoadingCache.this.idField)).->[java.util.Map[String,String]](rec.toMap())
75 53440 3075 - 3075 TypeApply scala.collection.mutable.Buffer.canBuildFrom mutable.this.Buffer.canBuildFrom[(String, java.util.Map[String,String])]
75 53441 3114 - 3114 TypeApply scala.Predef.$conforms scala.Predef.$conforms[(String, java.util.Map[String,String])]
75 53442 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 53445 3187 - 3194 Apply scala.Array.apply args.apply(0)
79 53446 3206 - 3213 Apply scala.Array.apply args.apply(1)
79 53447 3200 - 3214 Apply java.util.Map.get x$3.get(args.apply(1))
79 53448 3216 - 3216 TypeApply scala.Predef.$conforms scala.Predef.$conforms[Null]
79 53449 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 53450 3283 - 3286 Select scala.Predef.??? scala.Predef.???
81 53451 3318 - 3321 Select scala.Predef.??? scala.Predef.???
82 53452 3353 - 3355 Literal <nosymbol> ()
86 53453 3490 - 3496 Literal <nosymbol> "type"
86 53454 3501 - 3542 Apply java.lang.String.equals conf.getString("type").equals("resource")
86 53455 3477 - 3542 Apply scala.Boolean.&& conf.hasPath("type").&&(conf.getString("type").equals("resource"))
91 53456 3661 - 3683 Apply com.typesafe.config.Config.getString conf.getString("path")
92 53457 3702 - 3728 Apply com.typesafe.config.Config.getString conf.getString("id-field")
93 53458 3747 - 3776 Apply com.typesafe.config.Config.getStringList conf.getStringList("columns")
94 53459 3821 - 3843 Select scala.collection.TraversableOnce.toList scala.collection.JavaConverters.asScalaBufferConverter[String](headers).asScala.toList
94 53460 3781 - 3844 Apply org.locationtech.geomesa.convert.ResourceLoadingCache.<init> new ResourceLoadingCache(path, idField, scala.collection.JavaConverters.asScalaBufferConverter[String](headers).asScala.toList)