I have some data I'm getting from an external source that I don't control and have been told that I have to map some of it to "user friendly" display values. So I wrote a little utility service to do this.
Populating selectors, I need to convert a list of values. Displaying data records, I have to convert individual values. And some values are obsolete and not shown in some circumstances. The records are stored in a database so that I can update the mapping at need.
I'm still learning how to write better Scala code, so comments on my use of idioms, error handling, and such would be especially helpful.
/**
* This service class maps data values from an external source to
* more user friendly display values, or hides them altogether (use = false).
*/
class ValueMapper(context: EFLContext, data: Node) {
val logger = context.findService(classOf[LoggerFactoryService]) match {
case None => null
case Some(loggerFactory) => loggerFactory.getLogger(getClass.getName)
}
val sourceName = data.attribute("source") match {
case None => throw new InvalidParameterException("Definition must include a 'source' attribute.'")
case Some(a) => a.head.toString()
}
val source = context.findService(sourceName, classOf[DBSessionFactoryService]) match {
case None => throw new InvalidParameterException("Context does not contain a source DB with the specified name. (" + sourceName + ")")
case Some(db) => db
}
val dataCache = new mutable.HashMap[String, Map[String, MappedValue]]
def get(typeID: String, key: String, ignoreUnused: Boolean = true): Option[String] = {
logger.trace("[get] looking for: {} in {}", key, typeID, "")
var result: Option[String] = None
for (
data <- cacheGet(typeID);
record <- data.get(key)
) {
if (record.getUse != "n" || !ignoreUnused) result = Some(record.getLabel)
}
result
}
def map(typeID: String, strings: List[String], ignoreUnused: Boolean = true): List[String] = {
logger.trace("[map] looking for: {}", typeID)
cacheGet(typeID) match {
case None => throw new IllegalArgumentException("Invalid type ID '" + typeID + "'.")
case Some(data) =>
strings.flatMap((key) => data.get(key).flatMap((record) => if (ignoreUnused && record.getUse == "n") None else Some(record.getLabel)))
}
}
private def cacheGet(typeID: String): Option[Map[String, MappedValue]] = {
logger.trace("[cacheGet] looking for: {}", typeID)
dataCache.get(typeID) match {
case Some(m) =>
logger.trace("[cacheGet] already in cache.")
Some(m)
case None =>
val session = source.createSession
val queryData = Map[String, AnyRef]("mapType" -> typeID)
val rawData = session.getUsingNamedQuery("findMappedValueByType", queryData)
if (rawData == null || rawData.length == 0) return None
val result = rawData.map{
case v: MappedValue => (v.getRefID, v)
}.toMap
session.close()
dataCache.put(typeID, result)
logger.trace("[cacheGet] added to cache.")
Some(result)
}
}
}
@Entity
@NamedQueries(
Array[NamedQuery](
new NamedQuery(name = "findMappedValueByType", query = "FROM MappedValue WHERE mapType = :mapType")
))
@Table(name = "MappedValue")
class MappedValue{
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name = "mvd_id", updatable = false)
@BeanProperty var id: Int = _
@Column(name = "mvd_type")
@BeanProperty var mapType: String = _
@Column(name = "mvd_ref_id")
@BeanProperty var refID: String = _
@Column(name = "mvd_use")
@BeanProperty var use: String = _
@Column(name = "mvd_label")
@BeanProperty var label: String = _
}
And the test class:
@RunWith(classOf[JUnitRunner])
class ValueMapperTest extends FunSuite with DBTest with BeforeAndAfter with Matchers {
var logger: Logger = null
var testService: ValueMapper = null
before {
ModuleLoader.shutdown()
Thread.sleep(500)
makeDBCopy("testPersStoreMaster1", "vmtPersStore")
val resourceURL = getClass.getResource("ValueMapperTestManifest.xml")
ModuleLoader.initialize(resourceURL)
val defaultContext = ModuleLoader.getDefaultContext
val loggerFactory = defaultContext.findService(classOf[LoggerFactoryService]).get
logger = loggerFactory.getLogger(getClass.getName)
defaultContext.findService(classOf[ValueMapper]) match {
case None => fail("Should have found service to test.")
case Some(s) => testService = s
}
}
val fromData = List("Raid", "Appliance", "IsilonProprietaryHST", "IsilonPublicHST", "IsilonPublicFUSE", "IsilonRefFilesHST", "SafeStoreCache")
val fullToData = List("Archive (Primary)", "Safe Store", "Isilon (HST Proprietary)", "Isilon (HST Public)", "Isilon (FUSE Public)", "Isilon (HST Ref Files)", "(obsolete) safe store cache")
val filteredToData = List("Archive (Primary)", "Safe Store", "Isilon (HST Proprietary)", "Isilon (HST Public)", "Isilon (FUSE Public)", "Isilon (HST Ref Files)")
test("mapping"){
var result = testService.map("StorageType", fromData, false)
result should be(fullToData)
result = testService.map("StorageType", fromData, true)
result should be(filteredToData)
an [IllegalArgumentException] should be thrownBy {testService.map("BadType", fromData, true)}
}
test("individual map") {
testService.get("StorageType", "Raid", false).get should be ("Archive (Primary)")
testService.get("StorageType", "SafeStoreCache") should be (None)
testService.get("BadType", "Something") should be (None)
}
}