【Cocos2d-x】cocos2d::Valueを使うのに便利なメソッドをいくつか作った

# `cocos2d::Value`の中身を良い感じに出力する

PHPの`var_dump()`みたいな感じ。

```cpp
void ValueHelper::dumpValue(const cocos2d::Value &value)
{
    CCLOG("%s", ValueHelper::getDumpString(value).c_str());
}

std::string ValueHelper::getDumpString(const cocos2d::Value &value,
                                       unsigned int level /* = 1 */)
{
    std::string ret;
    
    switch (value.getType()) {
        case Value::Type::NONE:    ret = "NONE,\n";                                                                 break;
        case Value::Type::BYTE:    ret = StringUtils::format("BYTE(%c),\n",    value.asByte());                     break;
        case Value::Type::INTEGER: ret = StringUtils::format("INTEGER(%d),\n", value.asInt());                      break;
        case Value::Type::FLOAT:   ret = StringUtils::format("FLOAT(%f),\n",   value.asFloat());                    break;
        case Value::Type::DOUBLE:  ret = StringUtils::format("DOUBLE(%f),\n",  value.asDouble());                   break;
        case Value::Type::BOOLEAN: ret = StringUtils::format("BOOLEAN(%s),\n", value.asBool() ? "true" : "false");  break;
        case Value::Type::STRING:  ret = StringUtils::format("STRING(%s),\n",  value.asString().c_str());           break;
            
        case Value::Type::VECTOR:
            ret += "VECTOR(" + StrUtils::intToString((int)value.asValueVector().size()) + ")(\n";
            for (Value child : value.asValueVector()) {
                // 階層によるインデント
                for (int i=0; i<level; i++) ret += "    ";
                ret += ValueHelper::getDumpString(child, level+1);
            }
            // 階層によるインデント
            for (int i=0; i<level-1; i++) ret += "    ";
            ret += "),\n";
            break;
            
        case Value::Type::MAP:
            ret += "MAP(" + StrUtils::intToString((int)value.asValueMap().size()) + "){\n";
            for (ValueMap::const_iterator itr = value.asValueMap().begin(); itr != value.asValueMap().end(); itr++) {
                // 階層によるインデント
                for (int i=0; i<level; i++) ret += "    ";
                ret += StringUtils::format("\"%s\": ", (*itr).first.c_str()) + ValueHelper::getDumpString((*itr).second, level+1);
            }
            // 階層によるインデント
            for (int i=0; i<level-1; i++) ret += "    ";
            ret += "},\n";
            break;
            
        case Value::Type::INT_KEY_MAP:
            ret += "INT_KEY_MAP(" + StrUtils::intToString((int)value.asIntKeyMap().size()) + "){\n";
            for (ValueMap::const_iterator itr = value.asValueMap().begin(); itr != value.asValueMap().end(); itr++) {
                // 階層によるインデント
                for (int i=0; i<level; i++) ret += "    ";
                ret += StringUtils::format("\"%s\": ", (*itr).first.c_str()) + ValueHelper::getDumpString((*itr).second, level+1);
            }
            // 階層によるインデント
            for (int i=0; i<level-1; i++) ret += "    ";
            ret += "},\n";
            break;
            
        default:
            NERROR("引数 value の getType() の値が不正です。: %d", value.getType());
            break;
    }
    
    return ret;
}
```

# `cocos2d::Value`の中身を細かく比較する

```cpp
bool ValueHelper::isEqual(const cocos2d::Value &value1,
                          const cocos2d::Value &value2)
{
    // 型が違えばもう違う
    if (value1.getType() != value2.getType()) return false;
    
    /***** 以下、value1とvalue2の型が等しいことは保証される *****/
    
    switch (value1.getType()) {
        case Value::Type::NONE:        return true;     // 中身が無いので比較する必要無し
        case Value::Type::BYTE:        return (value1.asByte()   == value2.asByte());
        case Value::Type::INTEGER:     return (value1.asInt()    == value2.asInt());
        case Value::Type::FLOAT:       return (value1.asFloat()  == value2.asFloat());
        case Value::Type::DOUBLE:      return (value1.asDouble() == value2.asDouble());
        case Value::Type::BOOLEAN:     return (value1.asBool()   == value2.asBool());
        case Value::Type::STRING:      return (value1.asString() == value2.asString());
        case Value::Type::VECTOR:      return ValueHelper::isEqualVector(value1.asValueVector(), value2.asValueVector());
        case Value::Type::MAP:         return ValueHelper::isEqualMap(value1.asValueMap(), value2.asValueMap());
        case Value::Type::INT_KEY_MAP: return ValueHelper::isEqualIntKeyMap(value1.asIntKeyMap(), value2.asIntKeyMap());
    }
    
    return false;
}

bool ValueHelper::isEqualVector(const cocos2d::ValueVector vector1,
                                const cocos2d::ValueVector vector2,
                                const bool considerOrder /* = false */)
{
    // 要素数が違えばもう違う
    if (vector1.size() != vector2.size()) return false;
    
    /***** 以下、vector1とvector2の要素数が等しいことは保証される *****/
    
    if (considerOrder) {
        // 順序を考慮する場合
        // vector1, vector2の同じ要素番号に格納されている値が、全て同じなら同じ。
        for (unsigned int i=0; i<vector1.size(); i++)
            // 一つでも違えば違う。
            if (!ValueHelper::isEqual(vector1.at(i), vector2.at(i))) return false;
    }
    else {
        // 順序を考慮しない場合
        // 要素数が等しいことは保証されているので、value1の要素が全てvalue2に入っていれば同じ。
        for (Value childValue1 : vector1)
            // 一つでも違えば違う。
            if (!ValueHelper::isContainsValue(vector2, childValue1)) return false;
    }
    
    return true;
}


bool ValueHelper::isEqualMap(const cocos2d::ValueMap &map1,
                             const cocos2d::ValueMap &map2)
{
    // 要素数が違えばもう違う
    if (map1.size() != map2.size()) return false;
    
    /**** 以下、map1とmap2の要素数が等しいことは保証される *****/
    
    // 要素数が等しいことは保証されているので、value1の全ての要素について
    //   - 同じキーが有る
    //   - そのキーに対する値が等しい
    // なら同じ。
    for (std::pair<std::string, Value> pair : map1) {
        const std::string key        = pair.first;
        const Value       childValue = pair.second;
        // 一つでも違えば違う。
        if (!ValueHelper::isExistsKey(map2, key)
            ||
            !ValueHelper::isEqual(childValue, map2.at(key))) return false;
    }
    
    return true;
}


bool ValueHelper::isEqualIntKeyMap(const cocos2d::ValueMapIntKey &map1,
                                     const cocos2d::ValueMapIntKey &map2)
{
    // 要素数が違えばもう違う
    if (map1.size() != map2.size()) return false;
    
    /**** 以下、map1とmap2の要素数が等しいことは保証される *****/
    
    // 要素数が等しいことは保証されているので、value1の全ての要素について
    //   - 同じキーが有る
    //   - そのキーに対する値が等しい
    // なら同じ。
    for (std::pair<int, Value> pair : map1) {
        const int key          = pair.first;
        const Value childValue = pair.second;
        // 一つでも違えば違う。
        if (!ValueHelper::isExistsKey(map2, key)
            ||
            !ValueHelper::isEqual(childValue, map2.at(key))) return false;
    }
    
    return true;
}
```

# `cocos2d::ValueMap``cocos2d::ValueMapIntKey`の中に該当するキーが存在するか調べる

```cpp

bool ValueHelper::isExistsKey(const cocos2d::ValueMap &map,
                              const std::string &key)
{
    return (map.find(key) != map.end());
}

bool ValueHelper::isExistsKey(const cocos2d::ValueMapIntKey &map,
                              const int key)
{
    return (map.find(key) != map.end());
}
```

# `cocos2d::ValueVector`の中に該当する値があるか調べる

```cpp
bool ValueHelper::isContainsValue(const cocos2d::ValueVector &vector,
                                  const cocos2d::Value &value)
{
    for (Value comparison : vector)
        // 同値が見つかった時点でtrueを返却
        if (ValueHelper::isEqual(value, comparison)) return true;
    
    return false;
}
```