Add a framework for unifying the conversion of enumerated values to and from
string tokens. Use it in a few places.

* subversion/include/private/svn_token.h,
  subversion/libsvn_subr/token.c
  New files.

* subversion/svn/tree-conflicts.c
  (map_conflict_action_human, map_conflict_action_xml,
   map_conflict_reason_human, map_conflict_reason_xml): New mapping tables.
  (action_str, reason_str, svn_cl__append_tree_conflict_info_xml):
    Re-implement using the new token functions instead of in-line switches.

* subversion/svn/util.c
  (map_wc_operation_xml, map_wc_operation_human): New mapping tables.
  (svn_cl__operation_str_xml, svn_cl__operation_str_human_readable):
    Re-implement using the new token functions instead of in-line switches.
--This line, and those below, will be ignored--

Index: subversion/include/private/svn_token.h
===================================================================
--- subversion/include/private/svn_token.h	(revision 0)
+++ subversion/include/private/svn_token.h	(revision 0)
@@ -0,0 +1,94 @@
+/* svn_token.h : value/string-token functions
+ *
+ * ====================================================================
+ *    Licensed to the Subversion Corporation (SVN Corp.) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The SVN Corp. licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ */
+
+#ifndef SVN_TOKEN_H
+#define SVN_TOKEN_H
+
+
+#include "svn_error.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+/* A mapping between a string STR and an enumeration value VAL.
+ * (A token map is an array of these, terminated by an entry with STR = NULL.)
+ */
+typedef struct
+{
+  const char *str;
+  int val;
+} svn_token_map_t;
+
+
+/* A value used by some token functions to indicate an unrecognized token. */
+#define SVN_TOKEN_UNKNOWN (-9999)
+
+
+/* Return the string form of the given VALUE as found in MAP. If the value
+   is not recognized, then a MALFUNCTION will occur.  */
+const char *
+svn_token__to_word(const svn_token_map_t *map,
+                   int value);
+
+
+/* Return the integer value of the given token WORD, as found in MAP. If the
+   string is not recognized, then a MALFUNCTION will occur.
+
+   Note: this function is for persisted string values. Because this function
+   will throw a MALFUNCTION, it should not be used for network input or
+   user input.  */
+int
+svn_token__from_word_strict(const svn_token_map_t *map,
+                            const char *word);
+
+
+/* Store the integer value of WORD into *VALUE. If the string is not
+   recognized, return SVN_ERR_BAD_TOKEN.  */
+svn_error_t *
+svn_token__from_word_err(int *value,
+                         const svn_token_map_t *map,
+                         const char *word);
+
+
+/* Return the integer value of the given token WORD as found in MAP. If the
+   string is not recognized, return SVN_TOKEN_UNKNOWN.  */
+int
+svn_token__from_word(const svn_token_map_t *map,
+                     const char *word);
+
+
+/* Return the integer value of the given token string. If the string is not
+   recognized, return SVN_TOKEN_UNKNOWN.  */
+int
+svn_token__from_mem(const svn_token_map_t *map,
+                    const char *word,
+                    apr_size_t len);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* SVN_TOKEN_H */
Index: subversion/libsvn_subr/token.c
===================================================================
--- subversion/libsvn_subr/token.c	(revision 0)
+++ subversion/libsvn_subr/token.c	(revision 0)
@@ -0,0 +1,103 @@
+/*
+ * token.c :  value/string-token functions
+ *
+ * ====================================================================
+ *    Licensed to the Subversion Corporation (SVN Corp.) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The SVN Corp. licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ */
+
+#include "svn_types.h"
+#include "svn_error.h"
+
+#include "private/svn_token.h"
+#include "svn_private_config.h"
+
+/* Reach up to higher layers to grab enumeration values  */
+#include "svn_wc.h"
+#include "../libsvn_wc/wc_db.h"
+
+
+/* ### */
+#define SVN_ERR_BAD_TOKEN 54321
+
+
+const char *
+svn_token__to_word(const svn_token_map_t *map,
+                   int value)
+{
+  for (; map->str != NULL; ++map)
+    if (map->val == value)
+      return map->str;
+
+  /* Internal, numeric values should always be found.  */
+  SVN_ERR_MALFUNCTION_NO_RETURN();
+}
+
+
+int
+svn_token__from_word_strict(const svn_token_map_t *map,
+                            const char *word)
+{
+  int value = svn_token__from_word(map, word);
+
+  if (value == SVN_TOKEN_UNKNOWN)
+    SVN_ERR_MALFUNCTION_NO_RETURN();
+
+  return value;
+}
+
+
+svn_error_t *
+svn_token__from_word_err(int *value,
+                         const svn_token_map_t *map,
+                         const char *word)
+{
+  *value = svn_token__from_word(map, word);
+
+  if (*value == SVN_TOKEN_UNKNOWN)
+    return svn_error_createf(SVN_ERR_BAD_TOKEN, NULL,
+                             _("Token '%s' is unrecognized"),
+                             word);
+
+  return SVN_NO_ERROR;
+}
+
+
+int
+svn_token__from_word(const svn_token_map_t *map,
+                     const char *word)
+{
+  for (; map->str != NULL; ++map)
+    if (strcmp(map->str, word) == 0)
+      return map->val;
+
+  return SVN_TOKEN_UNKNOWN;
+}
+
+
+int
+svn_token__from_mem(const svn_token_map_t *map,
+                    const char *word,
+                    apr_size_t len)
+{
+  for (; map->str != NULL; ++map)
+    if (strncmp(map->str, word, len) == 0 && map->str[len] == '\0')
+      return map->val;
+
+  return SVN_TOKEN_UNKNOWN;
+}
Index: subversion/svn/tree-conflicts.c
===================================================================
--- subversion/svn/tree-conflicts.c	(revision 38964)
+++ subversion/svn/tree-conflicts.c	(working copy)
@@ -25,53 +25,74 @@
 #include "svn_xml.h"
 #include "svn_dirent_uri.h"
 #include "svn_path.h"
+#include "private/svn_token.h"
 
 #include "cl.h"
 
 #include "svn_private_config.h"
 
+
+/* A map for svn_wc_conflict_action_t values to human-readable strings */
+static const svn_token_map_t map_conflict_action_human[] =
+{
+  { N_("edited"),       svn_wc_conflict_action_edit },
+  { N_("deleted"),      svn_wc_conflict_action_delete },
+  { N_("added"),        svn_wc_conflict_action_add },
+  { N_("replace"),      svn_wc_conflict_action_replace },
+  { NULL,               0 }
+};
+
+/* A map for svn_wc_conflict_action_t values to XML strings */
+static const svn_token_map_t map_conflict_action_xml[] =
+{
+  { "edit",             svn_wc_conflict_action_edit },
+  { "delete",           svn_wc_conflict_action_delete },
+  { "add",              svn_wc_conflict_action_add },
+  { "replace",          svn_wc_conflict_action_replace },
+  { NULL,               0 }
+};
+
+/* A map for svn_wc_conflict_reason_t values to human-readable strings */
+static const svn_token_map_t map_conflict_reason_human[] =
+{
+  { N_("edited"),       svn_wc_conflict_reason_edited },
+  { N_("deleted"),      svn_wc_conflict_reason_deleted },
+  { N_("missing"),      svn_wc_conflict_reason_missing },
+  { N_("obstructed"),   svn_wc_conflict_reason_obstructed },
+  { N_("added"),        svn_wc_conflict_reason_added },
+  { N_("replaced"),     svn_wc_conflict_reason_replaced },
+  { N_("unversioned"),  svn_wc_conflict_reason_unversioned },
+  { NULL,               0 }
+};
+
+/* A map for svn_wc_conflict_reason_t values to XML strings */
+static const svn_token_map_t map_conflict_reason_xml[] =
+{
+  { "edit",             svn_wc_conflict_reason_edited },
+  { "delete",           svn_wc_conflict_reason_deleted },
+  { "missing",          svn_wc_conflict_reason_missing },
+  { "obstruction",      svn_wc_conflict_reason_obstructed },
+  { "add",              svn_wc_conflict_reason_added },
+  { "replace",          svn_wc_conflict_reason_replaced },
+  { "unversioned",      svn_wc_conflict_reason_unversioned },
+  { NULL,               0 }
+};
+
+/* Return a localized string representation of CONFLICT->action. */
 static const char *
 action_str(const svn_wc_conflict_description_t *conflict)
 {
-  switch (conflict->action)
-    {
-      /* Order of cases follows definition of svn_wc_conflict_action_t. */
-      case svn_wc_conflict_action_edit:
-        return _("edit");
-      case svn_wc_conflict_action_add:
-        return _("add");
-      case svn_wc_conflict_action_delete:
-        return _("delete");
-      case svn_wc_conflict_action_replace:
-        return _("replace");
-    }
-  return NULL;
+  return _(svn_token__to_word(map_conflict_action_human, conflict->action));
 }
 
+/* Return a localized string representation of CONFLICT->reason. */
 static const char *
 reason_str(const svn_wc_conflict_description_t *conflict)
 {
-  switch (conflict->reason)
-    {
-      /* Order of cases follows definition of svn_wc_conflict_reason_t. */
-      case svn_wc_conflict_reason_edited:
-        return _("edit");
-      case svn_wc_conflict_reason_obstructed:
-        return _("obstruction");
-      case svn_wc_conflict_reason_deleted:
-        return _("delete");
-      case svn_wc_conflict_reason_added:
-        return _("add");
-      case svn_wc_conflict_reason_missing:
-        return _("missing");
-      case svn_wc_conflict_reason_unversioned:
-        return _("unversioned");
-      case svn_wc_conflict_reason_replaced:
-        return _("replace");
-    }
-  return NULL;
+  return _(svn_token__to_word(map_conflict_reason_human, conflict->reason));
 }
 
+
 svn_error_t *
 svn_cl__get_human_readable_tree_conflict_description(
   const char **desc,
@@ -145,53 +166,10 @@ svn_cl__append_tree_conflict_info_xml(
   apr_hash_set(att_hash, "operation", APR_HASH_KEY_STRING,
                svn_cl__operation_str_xml(conflict->operation, pool));
 
-  switch (conflict->action)
-    {
-      /* Order of cases follows definition of svn_wc_conflict_action_t. */
-      case svn_wc_conflict_action_edit:
-        tmp = "edit";
-        break;
-      case svn_wc_conflict_action_add:
-        tmp = "add";
-        break;
-      case svn_wc_conflict_action_delete:
-        tmp = "delete";
-        break;
-      case svn_wc_conflict_action_replace:
-        tmp = "replace";
-        break;
-      default:
-        SVN_ERR_MALFUNCTION();
-    }
+  tmp = svn_token__to_word(map_conflict_action_xml, conflict->action);
   apr_hash_set(att_hash, "action", APR_HASH_KEY_STRING, tmp);
 
-  switch (conflict->reason)
-    {
-      /* Order of cases follows definition of svn_wc_conflict_reason_t. */
-      case svn_wc_conflict_reason_edited:
-        tmp = "edit";
-        break;
-      case svn_wc_conflict_reason_obstructed:
-        tmp = "obstruction";
-        break;
-      case svn_wc_conflict_reason_deleted:
-        tmp = "delete";
-        break;
-      case svn_wc_conflict_reason_added:
-        tmp = "add";
-        break;
-      case svn_wc_conflict_reason_missing:
-        tmp = "missing";
-        break;
-      case svn_wc_conflict_reason_unversioned:
-        tmp = "unversioned";
-        break;
-      case svn_wc_conflict_reason_replaced:
-        tmp = "replace";
-        break;
-      default:
-        SVN_ERR_MALFUNCTION();
-    }
+  tmp = svn_token__to_word(map_conflict_reason_xml, conflict->reason);
   apr_hash_set(att_hash, "reason", APR_HASH_KEY_STRING, tmp);
 
   /* Open the tree-conflict tag. */
Index: subversion/svn/util.c
===================================================================
--- subversion/svn/util.c	(revision 38964)
+++ subversion/svn/util.c	(working copy)
@@ -59,6 +59,8 @@
 #include "svn_private_config.h"
 #include "cl.h"
 
+#include "private/svn_token.h"
+
 
 
 
@@ -1028,70 +1030,70 @@ svn_cl__xml_print_footer(const char *tag
 }
 
 
+/* A map for svn_node_kind_t values to XML strings */
+static const svn_token_map_t map_node_kind_xml[] =
+{
+  { "none", svn_node_none },
+  { "file", svn_node_file },
+  { "dir",  svn_node_dir },
+  { "",     svn_node_unknown },
+  { NULL,   0 }
+};
+
+/* A map for svn_node_kind_t values to human-readable strings */
+static const svn_token_map_t map_node_kind_human[] =
+{
+  { N_("none"), svn_node_none },
+  { N_("file"), svn_node_file },
+  { N_("dir"),  svn_node_dir },
+  { N_(""),     svn_node_unknown },
+  { NULL,       0 }
+};
+
 const char *
 svn_cl__node_kind_str_xml(svn_node_kind_t kind)
 {
-  switch (kind)
-    {
-    case svn_node_none:
-      return "none";
-    case svn_node_dir:
-      return "dir";
-    case svn_node_file:
-      return "file";
-    default:
-      return "";
-    }
+  return svn_token__to_word(map_node_kind_xml, kind);
 }
 
 const char *
 svn_cl__node_kind_str_human_readable(svn_node_kind_t kind)
 {
-  switch (kind)
-    {
-    case svn_node_none:
-      return _("none");
-    case svn_node_dir:
-      return _("dir");
-    case svn_node_file:
-      return _("file");
-    default:
-      return "";
-    }
+  return _(svn_token__to_word(map_node_kind_human, kind));
 }
 
 
+/* A map for svn_wc_operation_t values to XML strings */
+static const svn_token_map_t map_wc_operation_xml[] =
+{
+  { "none",   svn_wc_operation_none },
+  { "update", svn_wc_operation_update },
+  { "switch", svn_wc_operation_switch },
+  { "merge",  svn_wc_operation_merge },
+  { NULL,     0 }
+};
+
+/* A map for svn_wc_operation_t values to human-readable strings */
+static const svn_token_map_t map_wc_operation_human[] =
+{
+  { N_("none"),   svn_wc_operation_none },
+  { N_("update"), svn_wc_operation_update },
+  { N_("switch"), svn_wc_operation_switch },
+  { N_("merge"),  svn_wc_operation_merge },
+  { NULL,         0 }
+};
+
 const char *
 svn_cl__operation_str_xml(svn_wc_operation_t operation, apr_pool_t *pool)
 {
-  switch(operation){
-	case svn_wc_operation_none:
-      return "none";
-    case svn_wc_operation_update:
-      return "update";
-    case svn_wc_operation_switch:
-      return "switch";
-    case svn_wc_operation_merge:
-      return "merge";
-  }
-  return "unknown_operation";
+  return svn_token__to_word(map_wc_operation_xml, operation);
 }
 
 const char *
 svn_cl__operation_str_human_readable(svn_wc_operation_t operation,
                                      apr_pool_t *pool)
 {
-  switch(operation){
-	case svn_wc_operation_none:
-      return _("none");
-    case svn_wc_operation_update:
-      return _("update");
-    case svn_wc_operation_switch:
-      return _("switch");
-    case svn_wc_operation_merge:
-      return _("merge");
-  }
-  return _("unknown operation");
+  return _(svn_token__to_word(map_wc_operation_human, operation));
 }
 
 

