Towards fixing AU preset invalidation
[ardour.git] / libs / pbd / enumwriter.cc
index c42cc3a5c242c13bb198539cf6b89f7e177f9228..7b3aba9053bafc4b956b7b4194cec809b4e97915 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
     Copyright (C) 2006 Paul Davis
 
     This program is free software; you can redistribute it and/or modify
     $Id$
 */
 
-#include <pbd/enumwriter.h>
-#include <pbd/error.h>
-#include <pbd/compose.h>
+#include <cctype>
+#include <algorithm>
+
+#include <cstring>
+#include <cstdlib>
+
+#include "pbd/enumwriter.h"
+#include "pbd/error.h"
+#include "pbd/compose.h"
 
 using namespace std;
 using namespace PBD;
 
-#include "i18n.h"
+#include "pbd/i18n.h"
 
 EnumWriter* EnumWriter::_instance = 0;
+map<string,string> EnumWriter::hack_table;
 
-EnumWriter::EnumWriter ()
+static int
+nocase_cmp(const string & s1, const string& s2)
+{
+       string::const_iterator it1 = s1.begin();
+       string::const_iterator it2 = s2.begin();
+
+       while ((it1 != s1.end()) && (it2 != s2.end())) {
+               if(::toupper(*it1) != ::toupper(*it2))  {//letters differ?
+                       // return -1 to indicate 'smaller than', 1 otherwise
+                       return (::toupper(*it1) < ::toupper(*it2)) ? -1 : 1;
+               }
+
+               ++it1;
+               ++it2;
+       }
+
+       string::size_type size1 = s1.size();
+       string::size_type size2 = s2.size();
+
+       //return -1,0 or 1 according to strings' lengths
+
+       if (size1 == size2) {
+               return 0;
+       }
+
+       return (size1 < size2) ? -1 : 1;
+}
+
+EnumWriter&
+EnumWriter::instance()
 {
        if (_instance == 0) {
-               _instance = this;
+               _instance = new EnumWriter;
        }
+
+       return *_instance;
+}
+
+void
+EnumWriter::destroy ()
+{
+       delete _instance;
+       _instance = 0;
+}
+
+EnumWriter::EnumWriter ()
+{
 }
 
 EnumWriter::~EnumWriter ()
@@ -48,7 +97,7 @@ EnumWriter::register_distinct (string type, vector<int> v, vector<string> s)
 
        newpair.first = type;
        newpair.second = EnumRegistration (v, s, false);
-       
+
        result = registry.insert (newpair);
 
        if (!result.second) {
@@ -64,7 +113,7 @@ EnumWriter::register_bits (string type, vector<int> v, vector<string> s)
 
        newpair.first = type;
        newpair.second = EnumRegistration (v, s, true);
-       
+
        result = registry.insert (newpair);
 
        if (!result.second) {
@@ -79,7 +128,7 @@ EnumWriter::write (string type, int value)
 
        if (x == registry.end()) {
                error << string_compose (_("EnumWriter: unknown enumeration type \"%1\""), type) << endmsg;
-               throw unknown_enumeration();
+               throw unknown_enumeration (type);
        }
 
        if (x->second.bitwise) {
@@ -96,7 +145,7 @@ EnumWriter::read (string type, string value)
 
        if (x == registry.end()) {
                error << string_compose (_("EnumWriter: unknown enumeration type \"%1\""), type) << endmsg;
-               throw unknown_enumeration();
+               throw unknown_enumeration (type);
        }
 
        if (x->second.bitwise) {
@@ -104,7 +153,7 @@ EnumWriter::read (string type, string value)
        } else {
                return read_distinct (x->second, value);
        }
-}      
+}
 
 string
 EnumWriter::write_bits (EnumRegistration& er, int value)
@@ -117,7 +166,7 @@ EnumWriter::write_bits (EnumRegistration& er, int value)
                if (value & (*i)) {
                        if (!result.empty()) {
                                result += ',';
-                       } 
+                       }
                        result += (*s);
                }
        }
@@ -140,6 +189,56 @@ EnumWriter::write_distinct (EnumRegistration& er, int value)
        return string();
 }
 
+int
+EnumWriter::validate (EnumRegistration& er, int val) const
+{
+        if (er.values.empty()) {
+                return val;
+        }
+
+        if (val == 0) {
+                /* zero is always a legal value for our enumerations, just about
+                 */
+                return val;
+        }
+
+        vector<int>::iterator i;
+        string enum_name = _("unknown enumeration");
+
+        for (Registry::const_iterator x = registry.begin(); x != registry.end(); ++x) {
+                if (&er == &(*x).second) {
+                        enum_name = (*x).first;
+                }
+        }
+
+
+        for (i = er.values.begin(); i != er.values.end(); ++i) {
+                if (*i == val) {
+                        return val;
+                }
+        }
+
+        warning << string_compose (_("Illegal value loaded for %1 (%2) - %3 used instead"),
+                                   enum_name, val, er.names.front())
+                << endmsg;
+        return er.values.front();
+}
+
+int
+EnumWriter::validate_bitwise (EnumRegistration& er, int val) const
+{
+       int result = 0;
+       for (int p = 1; p <= val; p = p << 1) {
+               if (std::find (er.values.begin(), er.values.end(), p) == er.values.end()) {
+                       continue;
+               }
+               if (p & val) {
+                       result |= p;
+               }
+       }
+       return result;
+}
+
 int
 EnumWriter::read_bits (EnumRegistration& er, string str)
 {
@@ -149,13 +248,27 @@ EnumWriter::read_bits (EnumRegistration& er, string str)
        bool found = false;
        string::size_type comma;
 
+       /* catch old-style hex numerics */
+
+       if (str.length() > 2 && str[0] == '0' && str[1] == 'x') {
+               int val = strtol (str.c_str(), (char **) 0, 16);
+                return validate_bitwise (er, val);
+       }
+
+       /* catch old style dec numerics */
+
+       if (strspn (str.c_str(), "0123456789") == str.length()) {
+               int val = strtol (str.c_str(), (char **) 0, 10);
+                return validate_bitwise (er, val);
+        }
+
        do {
-               
+
                comma = str.find_first_of (',');
                string segment = str.substr (0, comma);
 
                for (i = er.values.begin(), s = er.names.begin(); i != er.values.end(); ++i, ++s) {
-                       if (segment == (*s)) {
+                       if (segment == *s || nocase_cmp (segment, *s) == 0) {
                                result |= (*i);
                                found = true;
                        }
@@ -170,7 +283,7 @@ EnumWriter::read_bits (EnumRegistration& er, string str)
        } while (true);
 
        if (!found) {
-               throw unknown_enumeration();
+               throw unknown_enumeration (str);
        }
 
        return result;
@@ -182,13 +295,48 @@ EnumWriter::read_distinct (EnumRegistration& er, string str)
        vector<int>::iterator i;
        vector<string>::iterator s;
 
+       /* first, check to see if there a hack for the name we're looking up */
+
+       map<string,string>::iterator x;
+
+       if ((x  = hack_table.find (str)) != hack_table.end()) {
+
+               cerr << "found hack for " << str << " = " << x->second << endl;
+
+               str = x->second;
+
+               for (i = er.values.begin(), s = er.names.begin(); i != er.values.end(); ++i, ++s) {
+                       if (str == (*s) || nocase_cmp (str, *s) == 0) {
+                               return (*i);
+                       }
+               }
+       }
+
+       /* catch old-style hex numerics */
+
+       if (str.length() > 2 && str[0] == '0' && str[1] == 'x') {
+               int val = strtol (str.c_str(), (char **) 0, 16);
+                return validate (er, val);
+       }
+
+       /* catch old style dec numerics */
+
+       if (strspn (str.c_str(), "0123456789") == str.length()) {
+               int val = strtol (str.c_str(), (char **) 0, 10);
+                return validate (er, val);
+        }
+
        for (i = er.values.begin(), s = er.names.begin(); i != er.values.end(); ++i, ++s) {
-               if (str == (*s)) {
+               if (str == (*s) || nocase_cmp (str, *s) == 0) {
                        return (*i);
                }
        }
 
-       throw unknown_enumeration();
+       throw unknown_enumeration(str);
 }
 
-
+void
+EnumWriter::add_to_hack_table (string str, string hacked)
+{
+       hack_table[str] = hacked;
+}