Change properties Enum to use a Map instead of a List

Change the _values list to a map so that we can get constant time lookup
of strings to Enum values. Additionally, override the toString method so
that it stores the toString value, instead of performing an expensive
lookup each time.

Results in about a 5% performance increase for files that convert
runtime values to enum values, e.g. runtime byte order in PCAP files.

DFDL-1225
diff --git a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/ElementBaseGrammarMixin.scala b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/ElementBaseGrammarMixin.scala
index fcf97e0..b78f417 100644
--- a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/ElementBaseGrammarMixin.scala
+++ b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/ElementBaseGrammarMixin.scala
@@ -500,7 +500,7 @@
 
   lazy val binary = {
     subset(lengthKind == LengthKind.Explicit, "Currently only lengthKind='explicit' is supported.")
-    LengthKind(lengthKind.toString(), this)
+    LengthKind(lengthKind.toString, this)
   }
 
   val bin = BinaryNumberRep.Binary // shorthands for table dispatch
diff --git a/daffodil-core/src/test/scala-new/edu/illinois/ncsa/daffodil/dsom/TestDsomCompiler3.scala b/daffodil-core/src/test/scala-new/edu/illinois/ncsa/daffodil/dsom/TestDsomCompiler3.scala
index 7cacdc4..828c0ef 100644
--- a/daffodil-core/src/test/scala-new/edu/illinois/ncsa/daffodil/dsom/TestDsomCompiler3.scala
+++ b/daffodil-core/src/test/scala-new/edu/illinois/ncsa/daffodil/dsom/TestDsomCompiler3.scala
@@ -74,28 +74,37 @@
       </xs:complexType>)
 
     val tmpDir = new File("./dfdl_tmp")
-    tmpDir.mkdirs
-    tmpDir.deleteOnExit()
-    
-    val sset = Compiler().compileNode(sc, Some(tmpDir)).sset
-    
-    val list = tmpDir.list()
-    assertEquals(1, list.length)
-    
-    val fileName = list(0)
-    assertTrue(fileName.contains(".dfdl.xsd"))
+    if (tmpDir.exists) {
+      tmpDir.listFiles.foreach(_.delete)
+      tmpDir.delete
+    }
+    try {
+      tmpDir.mkdirs
+      val sset = Compiler().compileNode(sc, Some(tmpDir)).sset
+      
+      val list = tmpDir.list()
+      assertEquals(1, list.length)
+      
+      val fileName = list(0)
+      assertTrue(fileName.contains(".dfdl.xsd"))
 
-    // Verify things still work using specified tmpDir
-    //
-    val Seq(schema) = sset.schemas
-    val Seq(schemaDoc, _) = schema.schemaDocuments
-    val Seq(declFactory) = schemaDoc.globalElementDecls
-    val decl = declFactory.forRoot()
-    val Seq(ct) = schemaDoc.globalComplexTypeDefs
-    assertEquals("example1", ct.name)
+      // Verify things still work using specified tmpDir
+      //
+      val Seq(schema) = sset.schemas
+      val Seq(schemaDoc, _) = schema.schemaDocuments
+      val Seq(declFactory) = schemaDoc.globalElementDecls
+      val decl = declFactory.forRoot()
+      val Seq(ct) = schemaDoc.globalComplexTypeDefs
+      assertEquals("example1", ct.name)
 
-    val fa = decl.formatAnnotation.asInstanceOf[DFDLElement]
-    assertEquals(AlignmentUnits.Bytes, decl.alignmentUnits)
+      val fa = decl.formatAnnotation.asInstanceOf[DFDLElement]
+      assertEquals(AlignmentUnits.Bytes, decl.alignmentUnits)
+    } finally {
+      if (tmpDir.exists) {
+        tmpDir.listFiles.foreach(_.delete)
+        tmpDir.delete
+      }
+    }
   }
   
   @Test def testSlots1() {
@@ -154,4 +163,4 @@
     assertEquals(1, x_erd.slotIndexInParent)
   }
 
-}
\ No newline at end of file
+}
diff --git a/daffodil-core/src/test/scala/edu/illinois/ncsa/daffodil/dsom/TestDsomCompiler.scala b/daffodil-core/src/test/scala/edu/illinois/ncsa/daffodil/dsom/TestDsomCompiler.scala
index 6d418c0..62b1c59 100644
--- a/daffodil-core/src/test/scala/edu/illinois/ncsa/daffodil/dsom/TestDsomCompiler.scala
+++ b/daffodil-core/src/test/scala/edu/illinois/ncsa/daffodil/dsom/TestDsomCompiler.scala
@@ -261,7 +261,7 @@
     val e2 = e2f.forRoot()
     val e3 = e3f.forRoot()
     assertEquals(
-      ByteOrder.BigEndian.toString().toLowerCase(),
+      ByteOrder.BigEndian.toString.toLowerCase(),
       e1.formatAnnotation.asInstanceOf[DFDLElement].getProperty("byteOrder").toLowerCase())
     val Seq(a1, a2) = e3.annotationObjs // third one has two annotations
     assertTrue(a2.isInstanceOf[DFDLAssert]) // second annotation is newVariableInstance
diff --git a/daffodil-lib/src/main/scala/edu/illinois/ncsa/daffodil/schema/annotation/props/Properties.scala b/daffodil-lib/src/main/scala/edu/illinois/ncsa/daffodil/schema/annotation/props/Properties.scala
index 8f1f2ac..1dbee53 100644
--- a/daffodil-lib/src/main/scala/edu/illinois/ncsa/daffodil/schema/annotation/props/Properties.scala
+++ b/daffodil-lib/src/main/scala/edu/illinois/ncsa/daffodil/schema/annotation/props/Properties.scala
@@ -104,31 +104,29 @@
 abstract class Enum[A] extends EnumBase {
   class Value extends EnumValueBase { self: A => {
       val theVal = this
-      _values :+= theVal
+      _values += (getNameFromClass(this).toLowerCase -> theVal)
       _values
     }
-  }
-
-  def toPropName(prop: A) = {
-    val s = prop.toString
-    val capS = s.substring(0, 1).toLowerCase()
-    val propName = capS + s.substring(1)
-    propName
-  }
-
-  private var _values = List.empty[A]
-  def stringToEnum(enumTypeName: String, str: String, context: ThrowsSDE) = {
-    val opt = _values.find(_.toString.toLowerCase() == str.toLowerCase)
-    opt match {
-      case Some(e) => e
-      case None =>
-        context.SDE("Unknown property value ", enumTypeName, str)
+    override val toString = {
+      val s = getNameFromClass(this)
+      val capS = s.substring(0, 1).toLowerCase()
+      val propName = capS + s.substring(1)
+      propName
     }
   }
+
+  def toPropName(prop: A) = prop.toString
+
+  private var _values = scala.collection.mutable.Map[String, A]()
+  def stringToEnum(enumTypeName: String, str: String, context: ThrowsSDE) = {
+    val opt = _values.getOrElse(str.toLowerCase, context.SDE("Unknown value for %s property: %s", enumTypeName, str))
+    opt
+  }
+
   /**
    * Useful for diagnostic messages where you want to say "must be one of ...." and list the possibilities.
    */
-  def allValues = _values
+  def allValues = _values.toList.map { case (k, v) => v }
 
   /**
    * Scala delays construction of case objects (presumably because many programs don't use them at all)
diff --git a/daffodil-lib/src/test/scala/edu/illinois/ncsa/daffodil/schema/annotation/props/TestGeneratedProperties.scala b/daffodil-lib/src/test/scala/edu/illinois/ncsa/daffodil/schema/annotation/props/TestGeneratedProperties.scala
index 9f34775..ff59568 100644
--- a/daffodil-lib/src/test/scala/edu/illinois/ncsa/daffodil/schema/annotation/props/TestGeneratedProperties.scala
+++ b/daffodil-lib/src/test/scala/edu/illinois/ncsa/daffodil/schema/annotation/props/TestGeneratedProperties.scala
@@ -121,11 +121,7 @@
   }
 
   def comparePropValue(prop: Any, value: String) = {
-    if (prop.isInstanceOf[EnumValueBase]) {
-      val withInitialUpcase = initialUpperCase(value)
-      assertEquals(withInitialUpcase, prop.toString)
-    } else
-      assertEquals(value, prop.toString)
+    assertEquals(value, prop.toString)
   }
 
   @Test
@@ -154,7 +150,7 @@
     comparePropValue(hasProps.textBidiSymmetric, "yes")
     comparePropValue(hasProps.textBidiTextShaped, "no")
     comparePropValue(hasProps.textBidiNumeralShapes, "nominal")
-    comparePropValue(hasProps.textBidiOrientation, "RTL")
+    comparePropValue(hasProps.textBidiOrientation, "rTL")
     comparePropValue(hasProps.textStringJustification, "left")
     //comparePropValue(hasProps.textStringPadCharacter, "%SP;")
     comparePropValue(hasProps.truncateSpecifiedLengthString, "no")
@@ -187,7 +183,7 @@
     comparePropValue(hasProps.calendarCheckPolicy, "lax")
     comparePropValue(hasProps.calendarTimeZone, "UTC")
     comparePropValue(hasProps.calendarObserveDST, "yes")
-    comparePropValue(hasProps.calendarFirstDayOfWeek, "Monday")
+    comparePropValue(hasProps.calendarFirstDayOfWeek, "monday")
     comparePropValue(hasProps.calendarDaysInFirstWeek, "4")
     comparePropValue(hasProps.calendarCenturyStart, "53")
     comparePropValue(hasProps.calendarLanguage, "en-US")
diff --git a/daffodil-test/src/test/resources/edu/illinois/ncsa/daffodil/section23/runtime_properties/runtime-properties.tdml b/daffodil-test/src/test/resources/edu/illinois/ncsa/daffodil/section23/runtime_properties/runtime-properties.tdml
index 6956566..fb6b94a 100644
--- a/daffodil-test/src/test/resources/edu/illinois/ncsa/daffodil/section23/runtime_properties/runtime-properties.tdml
+++ b/daffodil-test/src/test/resources/edu/illinois/ncsa/daffodil/section23/runtime_properties/runtime-properties.tdml
@@ -209,7 +209,7 @@
     </tdml:document>
     <tdml:errors>
       <tdml:error>Schema Definition Error</tdml:error>
-      <tdml:error>Unknown Property Value</tdml:error>
+      <tdml:error>Unknown value for byteOrder property: middleEndian</tdml:error>
     </tdml:errors>
   </tdml:parserTestCase>
 
@@ -503,7 +503,7 @@
     </tdml:document>
     <tdml:errors>
       <tdml:error>Schema Definition Error</tdml:error>
-      <tdml:error>Unknown property value</tdml:error>
+      <tdml:error>Unknown value for byteOrder property: fatEndian</tdml:error>
     </tdml:errors>
 
   </tdml:parserTestCase>