ZFCompareResult ZFVersionCompare(ZF_IN const zfchar *version0,
                                 ZF_IN const zfchar *version1,
                                 ZF_IN_OPT ZFComparer<const zfchar *>::Comparer subVersionComparer /* = ZFComparerForVersion */)
{
    ZFCoreArrayPOD<zfindexRange> pos0;
    ZFCoreArrayPOD<zfindexRange> pos1;
    if(zfCoreDataPairSplitString(pos0, zfindexMax, version0, zfindexMax, '\0', '\0', zfText(".")) != zfnull
       || zfCoreDataPairSplitString(pos1, zfindexMax, version1, zfindexMax, '\0', '\0', zfText(".")) != zfnull)
    {
        return ZFCompareUncomparable;
    }

    zfindex count = zfmMin(pos0.count(), pos1.count());
    for(zfindex i = 0; i < count; ++i)
    {
        ZFCompareResult cmp = subVersionComparer(
            zfstring(version0 + pos0[i].start, pos0[i].count).cString()
            ,
            zfstring(version1 + pos1[i].start, pos1[i].count).cString()
            );
        switch(cmp)
        {
            case ZFCompareUncomparable:
            case ZFCompareSmaller:
            case ZFCompareGreater:
                return cmp;
            case ZFCompareTheSame:
                continue;
            default:
                zfCoreCriticalShouldNotGoHere();
                return ZFCompareUncomparable;
        }
    }
    if(pos0.count() > pos1.count())
    {
        return ZFCompareGreater;
    }
    else if(pos0.count() < pos1.count())
    {
        return ZFCompareSmaller;
    }
    else
    {
        return ZFCompareTheSame;
    }
}
ZF_NAMESPACE_GLOBAL_BEGIN

#if 1
ZF_GLOBAL_INITIALIZER_INIT(ZFCore_ZFSerializable_debug_ErrorLog)
{
    ZFCALLBACK_LOCAL_BEGIN_2(zfindex, tmp, const void *, param0, zfindex, param1)
    {
        zfLogTrimT() << zfstring((const zfchar *)param0, param1);
        return zfindexMax;
    }
/*
 * // '_' if null for encoded data
 *
 * {
 *     ClassNameEncoded
 *     (
 *         refTypeEncoded : refDataEncoded
 *     )
 *     (
 *         AttributeNameEncoded = AttributeValueEncoded;
 *         AttributeNameEncoded = AttributeValueEncoded;
 *     )
 *     [
 *         {ReferenceData},
 *         {ChildElement0},
 *         {ChildElement1}
 *     ]
 * }
 */
zfbool _ZFP_ZFSerializableDataFromString(ZF_OUT ZFSerializableData &serializableData,
                                         ZF_IN_OUT const zfchar *&encodedData,
                                         ZF_IN zfindex encodedDataLen,
                                         ZF_OUT zfstring *outErrorHintToAppend,
                                         ZF_IN_OPT zfbool validateTail = zffalse)
{
    if(encodedData == zfnull)
    {
        ZFSerializableUtil::errorOccurred(outErrorHintToAppend,
            zfText("invalid param"));
        return zffalse;
    }
    const zfchar *srcEnd = (encodedData + ((encodedDataLen == zfindexMax) ? zfslen(encodedData) : encodedDataLen));
    zfbool ret = zffalse;
    do
    {
        const zfchar *pLeft = zfnull;
        const zfchar *pRight = zfnull;
        zfstring decodedTmp;

        zfcharSkipSpaceAndEndl(encodedData, srcEnd);
        if(encodedData >= srcEnd || *encodedData != '{') {break;}
        ++encodedData;

        // class name
        zfcharSkipSpaceAndEndl(encodedData, srcEnd);
        pLeft = pRight = encodedData;
        while(*encodedData != '(' && encodedData < srcEnd)
        {
            if(!zfcharIsSpace(*encodedData))
            {
                zfcharMoveNext(encodedData);
                pRight = encodedData;
            }
            else
            {
                zfcharMoveNext(encodedData);
            }
        }
        if(encodedData >= srcEnd || *encodedData != '(') {break;}
        ++encodedData;

        if(pRight == pLeft + 1 && *pLeft == '_')
        {
            serializableData.itemClassSet(zfnull);
        }
        else
        {
            ZFCoreDataDecode(decodedTmp, zfstring(pLeft, pRight - pLeft).cString());
            serializableData.itemClassSet(decodedTmp.cString());
            decodedTmp.removeAll();
        }

        // refType
        zfcharSkipSpaceAndEndl(encodedData, srcEnd);
        pLeft = pRight = encodedData;
        while(*encodedData != ':' && encodedData < srcEnd)
        {
            if(!zfcharIsSpace(*encodedData))
            {
                zfcharMoveNext(encodedData);
                pRight = encodedData;
            }
            else
            {
                zfcharMoveNext(encodedData);
            }
        }
        if(encodedData >= srcEnd || *encodedData != ':') {break;}
        ++encodedData;

        if(pRight == pLeft + 1 && *pLeft == '_')
        {
            serializableData.referenceRefTypeSet(zfnull);
        }
        else
        {
            ZFCoreDataDecode(decodedTmp, zfstring(pLeft, pRight - pLeft).cString());
            serializableData.referenceRefTypeSet(decodedTmp.cString());
            decodedTmp.removeAll();
        }

        // refData
        zfcharSkipSpaceAndEndl(encodedData, srcEnd);
        pLeft = pRight = encodedData;
        while(*encodedData != ')' && encodedData < srcEnd)
        {
            if(!zfcharIsSpace(*encodedData))
            {
                zfcharMoveNext(encodedData);
                pRight = encodedData;
            }
            else
            {
                zfcharMoveNext(encodedData);
            }
        }
        if(encodedData >= srcEnd || *encodedData != ')') {break;}
        ++encodedData;

        if(pRight == pLeft + 1 && *pLeft == '_')
        {
            serializableData.referenceRefDataSet(zfnull);
        }
        else
        {
            ZFCoreDataDecode(decodedTmp, zfstring(pLeft, pRight - pLeft).cString());
            serializableData.referenceRefDataSet(decodedTmp.cString());
            decodedTmp.removeAll();
        }

        zfcharSkipSpaceAndEndl(encodedData, srcEnd);
        while(*encodedData != '(' && encodedData < srcEnd) {zfcharMoveNext(encodedData);}
        if(encodedData >= srcEnd || *encodedData != '(') {break;}
        ++encodedData;

        // attributes
        zfcharSkipSpaceAndEndl(encodedData, srcEnd);
        while(encodedData < srcEnd)
        {
            if(*encodedData == ')')
            {
                ++encodedData;
                ret = zftrue;
                break;
            }

            // name
            pLeft = pRight = encodedData;
            while(*encodedData != '=' && encodedData < srcEnd)
            {
                if(!zfcharIsSpace(*encodedData))
                {
                    zfcharMoveNext(encodedData);
                    pRight = encodedData;
                }
                else
                {
                    zfcharMoveNext(encodedData);
                }
            }
            if(encodedData >= srcEnd || *encodedData != '=') {break;}
            ++encodedData;

            zfstring attributeName;
            ZFCoreDataDecode(attributeName, zfstring(pLeft, pRight - pLeft).cString());

            // value
            zfcharSkipSpaceAndEndl(encodedData, srcEnd);
            pLeft = pRight = encodedData;
            while(*encodedData != ';' && encodedData < srcEnd)
            {
                if(!zfcharIsSpace(*encodedData))
                {
                    zfcharMoveNext(encodedData);
                    pRight = encodedData;
                }
                else
                {
                    zfcharMoveNext(encodedData);
                }
            }
            if(encodedData >= srcEnd || *encodedData != ';') {break;}
            ++encodedData;

            ZFCoreDataDecode(decodedTmp, zfstring(pLeft, pRight - pLeft).cString());

            // save
            if(!attributeName.isEmpty() && !decodedTmp.isEmpty())
            {
                serializableData.attributeSet(attributeName.cString(), decodedTmp.cString());
            }
            decodedTmp.removeAll();

            zfcharSkipSpaceAndEndl(encodedData, srcEnd);
        }
        if(!ret) {break;}
        ret = zffalse;

        zfcharSkipSpaceAndEndl(encodedData, srcEnd);
        if(encodedData >= srcEnd || *encodedData != '[') {break;}
        ++encodedData;

        // elements
        zfcharSkipSpaceAndEndl(encodedData, srcEnd);
        while(encodedData < srcEnd)
        {
            if(*encodedData == ']')
            {
                ++encodedData;
                ret = zftrue;
                break;
            }

            {
                ZFSerializableData element;
                if(!_ZFP_ZFSerializableDataFromString(element, encodedData, srcEnd - encodedData, outErrorHintToAppend))
                {
                    return zffalse;
                }
                serializableData.elementAdd(element);
            }

            zfcharSkipSpaceAndEndl(encodedData, srcEnd);
            if(encodedData >= srcEnd || (*encodedData != ',' && *encodedData != ']')) {break;}
            if(*encodedData == ',')
            {
                ++encodedData;
                zfcharSkipSpaceAndEndl(encodedData, srcEnd);
                if(encodedData >= srcEnd || *encodedData == ']') {break;}
            }
        }
        if(!ret) {break;}
        ret = zffalse;

        // tail
        zfcharSkipSpaceAndEndl(encodedData, srcEnd);
        if(encodedData >= srcEnd || *encodedData != '}') {break;}
        ++encodedData;
        if(validateTail)
        {
            zfcharSkipSpaceAndEndl(encodedData, srcEnd);
            if(encodedData < srcEnd) {break;}
        }

        ret = zftrue;
    } while(zffalse);
    if(!ret)
    {
        ZFSerializableUtil::errorOccurred(outErrorHintToAppend,
            zfText("wrong serializable string format at position: \"%s\""),
            zfstring(encodedData, srcEnd - encodedData).cString());
    }
    return ret;
}
zfbool ZFJsonToSerializableData(ZF_OUT ZFSerializableData &serializableData,
                                ZF_IN const ZFJsonItem *jsonObject,
                                ZF_OUT_OPT zfstring *outErrorHintToAppend /* = zfnull */,
                                ZF_OUT_OPT const ZFJsonItem **outErrorPos /* = zfnull */)
{
    if(jsonObject == zfnull)
    {
        ZFSerializableUtil::errorOccurred(outErrorHintToAppend, zfText("null json object"));
        if(outErrorPos != zfnull)
        {
            *outErrorPos = jsonObject;
        }
        return zffalse;
    }

    const ZFJsonItem *elementJsonArray = zfnull;
    for(zfiterator jsonItemIt = jsonObject->jsonItemIterator(); jsonObject->jsonItemIteratorIsValid(jsonItemIt); jsonObject->jsonItemIteratorNext(jsonItemIt))
    {
        const zfchar *key = jsonObject->jsonItemIteratorGetKey(jsonItemIt);
        const ZFJsonItem *jsonItem = jsonObject->jsonItemIteratorGet(jsonItemIt);
        zfassert(jsonItem != zfnull);
        if(*key == _ZFP_ZFJsonSerializeKey_classPrefix)
        {
            serializableData.itemClassSet(key + 1);

            if(jsonItem->jsonType() != ZFJsonType::e_JsonArray)
            {
                ZFSerializableUtil::errorOccurred(outErrorHintToAppend,
                    zfText("json item %s not type of %s"),
                    jsonItem->objectInfo().cString(),
                    ZFJsonType::EnumNameForValue(ZFJsonType::e_JsonArray));
                if(outErrorPos != zfnull)
                {
                    *outErrorPos = jsonItem;
                }
                return zffalse;
            }
            elementJsonArray = jsonItem;
        }
        else
        {
            if(jsonItem->jsonType() != ZFJsonType::e_JsonValue)
            {
                ZFSerializableUtil::errorOccurred(outErrorHintToAppend,
                    zfText("json item %s not type of %s"),
                    jsonItem->objectInfo().cString(),
                    ZFJsonType::EnumNameForValue(ZFJsonType::e_JsonValue));
                if(outErrorPos != zfnull)
                {
                    *outErrorPos = jsonItem;
                }
                return zffalse;
            }
            const zfchar *value = jsonItem->jsonValue();

            if(zfscmpTheSame(key, ZFSerializableKeyword_refType))
            {
                serializableData.referenceRefTypeSet(value);
            }
            else if(zfscmpTheSame(key, ZFSerializableKeyword_refData))
            {
                serializableData.referenceRefDataSet(value);
            }
            else
            {
                serializableData.attributeSet(zfstring(key).cString(), value);
            }
        }
    }

    if(serializableData.itemClass() == zfnull)
    {
        ZFSerializableUtil::errorOccurred(outErrorHintToAppend, zfText("missing class node (which looks like \"@ClassName\")"));
        if(outErrorPos != zfnull)
        {
            *outErrorPos = jsonObject;
        }
        return zffalse;
    }

    if(elementJsonArray != zfnull)
    {
        for(zfindex i = 0; i < elementJsonArray->jsonObjectCount(); ++i)
        {
            ZFSerializableData childData;
            if(!ZFJsonToSerializableData(childData, elementJsonArray->jsonObjectAtIndex(i), outErrorHintToAppend, outErrorPos))
            {
                return zffalse;
            }
            serializableData.elementAdd(childData);
        }
    }

    if(outErrorPos != zfnull)
    {
        *outErrorPos = zfnull;
    }
    return serializableData.referenceCheckLoad(outErrorHintToAppend, zfnull);
}