//+=================================================================================================================
//
// file :            Deviceclass.cpp
//
// description :    C++ source code for the DbServerCache class
//
// project :        TANGO
//
// author(s) :        E.Taurel
//
// Copyright (C) :      2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015
//                        European Synchrotron Radiation Facility
//                      BP 220, Grenoble 38043
//                      FRANCE
//
// This file is part of Tango.
//
// Tango is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Tango is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along with Tango.
// If not, see <http://www.gnu.org/licenses/>.
//
//
//-================================================================================================================

#include <tango/client/Database.h>
#include <tango/internal/parse.h>

using namespace CORBA;

namespace Tango
{

//------------------------------------------------------------------------------------------------------------------
//
// method:
//         DbServerCache::DbServerCache()
//
// description:
//        Constructor of the DbServerCache class. This class manage a data cache used during the device server
//        process startup phase.
//
// arguments:
//         in :
//            - db : The database object
//            - ds_name : The device server name (exec_name/inst_name)
//            - host : The host name
//
//------------------------------------------------------------------------------------------------------------------

DbServerCache::DbServerCache(Database *db, const std::string &ds_name, const std::string &host)
{
    //
    // Get all data from database
    //

    try
    {
        received = db->fill_server_cache(ds_name, host);
    }
    catch(Tango::DevFailed &)
    {
        TANGO_LOG_DEBUG << "Got an exception while getting cache data from database !!" << std::endl;
        throw;
    }

    received.inout() >>= data_list;
    n_data = data_list->length();

    //
    // Extract the different blocks from the big list. First the stored procedure release nb
    // The release number is stored as:
    //        (major_number * 100) + minor_number
    // Ex: 1.9 -> 109
    //

    proc_release = 0;
    std::string proc_rel((*data_list)[0]);
    if(proc_rel.find("release") != std::string::npos)
    {
        std::string rel = proc_rel.substr(8);
        std::string::size_type pos = rel.find('.');
        if(pos != std::string::npos)
        {
            std::string maj_str = rel.substr(0, pos);
            std::string min_str = rel.substr(pos + 1);

            int maj = detail::parse_as<int>(maj_str);
            int min = detail::parse_as<int>(min_str);

            proc_release = (maj * 100) + min;
        }
    }

    //
    // First, the device server admin device parameters
    //

    int start_idx = 0;
    int stop_idx = 0;

    if(proc_release != 0)
    {
        start_idx++;
        stop_idx++;
    }

    if(n_data == 2 || n_data == 3)
    {
        imp_adm.first_idx = start_idx;
        imp_adm.last_idx = start_idx + 1;

        ctrl_serv_prop.first_idx = -1;
        DServer_class_prop.first_idx = -1;
        Default_prop.first_idx = -1;
        adm_dev_prop.first_idx = -1;

        return;
    }

    stop_idx = start_idx + 7;
    imp_adm.first_idx = start_idx;
    imp_adm.last_idx = stop_idx;

    //
    // Event factory
    //

    start_idx = stop_idx + 1;
    if(::strcmp((*data_list)[start_idx + 1], "Not Found") == 0)
    {
        stop_idx = stop_idx + 2;
    }
    else
    {
        stop_idx = stop_idx + 6;
    }
    imp_notifd_event.first_idx = start_idx;
    imp_notifd_event.last_idx = stop_idx;

    //
    // Server event
    //

    start_idx = stop_idx + 1;
    if(::strcmp((*data_list)[start_idx + 1], "Not Found") == 0)
    {
        stop_idx = stop_idx + 2;
    }
    else
    {
        stop_idx = stop_idx + 6;
    }
    imp_adm_event.first_idx = start_idx;
    imp_adm_event.last_idx = stop_idx;

    //
    // DServer class prop
    //

    prop_indexes(start_idx, stop_idx, DServer_class_prop, data_list);

    //
    // Default prop
    //

    prop_indexes(start_idx, stop_idx, Default_prop, data_list);

    //
    // Admin device prop
    //

    prop_indexes(start_idx, stop_idx, adm_dev_prop, data_list);

    //
    // Embedded classes number
    //

    start_idx = stop_idx + 1;
    class_nb = detail::parse_as<int>((*data_list)[start_idx + 1].in());
    stop_idx = stop_idx + 2;
    classes_idx = new ClassEltIdx[class_nb];

    for(int cl_loop = 0; cl_loop < class_nb; cl_loop++)
    {
        //
        // Embedded class prop
        //

        prop_indexes(start_idx, stop_idx, classes_idx[cl_loop].class_prop, data_list);

        //
        // Embedded class attribute prop
        //

        prop_att_indexes(start_idx, stop_idx, classes_idx[cl_loop].class_att_prop, data_list);

        //
        // Embedded class pipe prop
        //

        if(proc_release >= 109)
        {
            prop_pipe_indexes(start_idx, stop_idx, classes_idx[cl_loop].class_pipe_prop, data_list);
        }
        else
        {
            classes_idx[cl_loop].class_pipe_prop.atts_idx = nullptr;
        }

        //
        // Device list
        //

        start_idx = stop_idx + 1;
        int nb_dev = detail::parse_as<int>((*data_list)[start_idx + 1].in());
        stop_idx = stop_idx + nb_dev + 2;

        classes_idx[cl_loop].dev_list.first_idx = start_idx;
        classes_idx[cl_loop].dev_list.last_idx = stop_idx;
        classes_idx[cl_loop].dev_nb = nb_dev;
        classes_idx[cl_loop].devs_idx = new DevEltIdx[nb_dev];

        for(int loop = 0; loop < nb_dev; loop++)
        {
            //
            // Device properties
            //

            prop_indexes(start_idx, stop_idx, classes_idx[cl_loop].devs_idx[loop].dev_prop, data_list);

            //
            // Device attribute properties
            //

            prop_att_indexes(start_idx, stop_idx, classes_idx[cl_loop].devs_idx[loop].dev_att_prop, data_list);

            //
            // Device pipe properties
            //

            if(proc_release >= 109)
            {
                prop_pipe_indexes(start_idx, stop_idx, classes_idx[cl_loop].devs_idx[loop].dev_pipe_prop, data_list);
            }
            else
            {
                classes_idx[cl_loop].devs_idx[loop].dev_pipe_prop.atts_idx = nullptr;
            }
        }
    }

    //
    // Control System Service(s) property
    //

    prop_indexes(start_idx, stop_idx, ctrl_serv_prop, data_list);

    //
    // Tac device import info
    //

    start_idx = stop_idx + 1;
    if(stop_idx == n_data)
    {
        stop_idx = -1;
    }
    else
    {
        stop_idx = n_data;
        imp_tac.first_idx = start_idx;
        imp_tac.last_idx = stop_idx;
    }
}

//------------------------------------------------------------------------------------------------------------------
//
// method:
//      DbServerCache::import_adm_dev()
//
// description:
//        This method returns to the caller of the info necessary to import the DS admin device. The returned data
//        are the same than the one returned by the classical API
//
// return:
//        This method returns a pointer to a DevVarLongStringArray initilised with the adm device import data
//
//------------------------------------------------------------------------------------------------------------------

const DevVarLongStringArray *DbServerCache::import_adm_dev()
{
    int last_index;
    if(proc_release >= 109)
    {
        last_index = 2;
    }
    else
    {
        last_index = 1;
    }

    if(imp_adm.last_idx == last_index)
    {
        TANGO_THROW_EXCEPTION("aaa", "bbb");
    }

    // We want to return a structure identical to what is returned by DbImportDevice, so
    // we return 6 strings and 2 longs, following this structure:
    //   svalue[0] = dev name (domain/family/member)
    //   svalue[1] = ior
    //   svalue[2] = version
    //   svalue[3] = server name (server name/instance name)
    //   svalue[4] = host
    //   svalue[5] = class
    //   lvalue[0] = exported (0/1)
    //   lvalue[1] = pid
    // HOWEVER, as of Tango-Database v5.23 (C++), the Database is returning only
    // strings from the call to DbGetDataForServerCache (which internally builds the
    // resulting string array with SQL procedures):
    //   [0] = db server version
    //   [1] = dev name (@imp_adm.first_idx)
    //   [2] = ior
    //   [3] = version
    //   [4] = server name
    //   [5] = host
    //   [6] = exported
    //   [7] = pid
    //   [8] = class  (IMPORTANT: not after 'host', @imp_adm.last_idx)

    imp_adm_data.lvalue.length(2);
    imp_adm_data.svalue.length(6);

    imp_adm_data.lvalue[0] = detail::parse_as<CORBA::Long>((*data_list)[imp_adm.first_idx + 5].in());
    imp_adm_data.lvalue[1] = detail::parse_as<CORBA::Long>((*data_list)[imp_adm.first_idx + 6].in());

    for(int loop = 0; loop < 5; loop++)
    {
        imp_adm_data.svalue[loop] = Tango::string_dup((*data_list)[imp_adm.first_idx + loop]);
    }

    // This is the 'class' name that is after everything!
    imp_adm_data.svalue[5] = Tango::string_dup((*data_list)[imp_adm.last_idx]);

    return &imp_adm_data;
}

//------------------------------------------------------------------------------------------------------------------
//
// method :
//      DbServerCache::import_notifd_event()
//
// description :
//        This method returns to the caller of the info necessary to import the event associated to the notifd running
//         on the DS host. The returned data are the same than the one returned by the classical API
//
// return :
//        This method returns a pointer to a DevVarLongStringArray initilised with the notifd event  import data
//
//------------------------------------------------------------------------------------------------------------------

const DevVarLongStringArray *DbServerCache::import_notifd_event()
{
    if(imp_notifd_event.last_idx == imp_notifd_event.first_idx + 1)
    {
        TANGO_THROW_EXCEPTION("aaa", "bbb");
    }

    imp_notifd_event_data.lvalue.length(2);
    imp_notifd_event_data.svalue.length(5);

    imp_notifd_event_data.lvalue[0] = detail::parse_as<CORBA::Long>((*data_list)[imp_notifd_event.first_idx + 4].in());
    imp_notifd_event_data.lvalue[1] = detail::parse_as<CORBA::Long>((*data_list)[imp_notifd_event.last_idx].in());

    for(int loop = 0; loop < 4; loop++)
    {
        imp_notifd_event_data.svalue[loop] = Tango::string_dup((*data_list)[imp_notifd_event.first_idx + loop]);
    }

    return &imp_notifd_event_data;
}

//-------------------------------------------------------------------------------------------------------------------
//
// method :
//         DbServerCache::import_adm_event()
//
// description :
//        This method returns to the caller of the info necessary to import the event associated to the DS server.
//        The returned data are the same than the one returned by the classical API
//
// return :
//        This method returns a pointer to a DevVarLongStringArray initilised with the DS event  import data
//
//-------------------------------------------------------------------------------------------------------------------

const DevVarLongStringArray *DbServerCache::import_adm_event()
{
    if(imp_adm_event.last_idx == imp_adm_event.first_idx + 1)
    {
        TANGO_THROW_EXCEPTION("aaa", "bbb");
    }

    imp_adm_event_data.lvalue.length(2);
    imp_adm_event_data.svalue.length(5);

    imp_adm_event_data.lvalue[0] = detail::parse_as<CORBA::Long>((*data_list)[imp_adm_event.first_idx + 4].in());
    imp_adm_event_data.lvalue[1] = detail::parse_as<CORBA::Long>((*data_list)[imp_notifd_event.last_idx].in());

    for(int loop = 0; loop < 4; loop++)
    {
        imp_adm_event_data.svalue[loop] = Tango::string_dup((*data_list)[imp_adm_event.first_idx + loop]);
    }

    return &imp_adm_event_data;
}

//-------------------------------------------------------------------------------------------------------------------
//
// method :
//      DbServerCache::get_class_property()
//
// description :
//        This method returns to the caller the wanted class properties. The returned data are the same than the one
//        returned by the classical API
//
// argument :
//         in :
//            - in_param : The class name followed by the wanted property names
//
// return :
//        This method returns a pointer to a DevVarStringArray initilised with the class properties
//
//-------------------------------------------------------------------------------------------------------------------

const DevVarStringArray *DbServerCache::get_class_property(DevVarStringArray *in_param)
{
    int ret_length = 2;

    if(TG_strcasecmp((*in_param)[0], "DServer") == 0)
    {
        ret_obj_prop.length(ret_length);
        ret_obj_prop[0] = Tango::string_dup("DServer");

        get_obj_prop(in_param, DServer_class_prop);
    }
    else if(TG_strcasecmp((*in_param)[0], "Default") == 0)
    {
        ret_obj_prop.length(ret_length);
        ret_obj_prop[0] = Tango::string_dup("Default");

        get_obj_prop(in_param, Default_prop);
    }
    else
    {
        ret_obj_prop.length(ret_length);
        ret_obj_prop[0] = Tango::string_dup((*in_param)[0]);

        int cl_idx = find_class((*in_param)[0]);
        if(cl_idx != -1)
        {
            get_obj_prop(in_param, classes_idx[cl_idx].class_prop);
        }
        else
        {
            TangoSys_OMemStream o;
            o << "Class " << (*in_param)[0] << " not found in DB cache" << std::ends;

            TANGO_THROW_EXCEPTION(DB_ClassNotFoundInCache, o.str());
        }
    }

    return &ret_obj_prop;
}

//------------------------------------------------------------------------------------------------------------------
//
// method:
//      DbServerCache::get_obj_prop()
//
// description :
//        This method searches the index(es) of the object wanted property within all the data returne by the DB server.
//         A structure will be initialised with the indexes
//
// argument :
//         in :
//            - in_param : The object name followed by the wanted property names
//            - dev_prop : Boolean set to true in case of device property. For device property, a undefined prop is
//                         returned with a value set to " " !
//      out :
//            - obj : Reference to the structure which will be initialsed by this method
//
//------------------------------------------------------------------------------------------------------------------

void DbServerCache::get_obj_prop(DevVarStringArray *in_param, PropEltIdx &obj, bool dev_prop)
{
    int ret_length = 2;
    int found_prop = 0;
    char n_prop_str[256];

    int nb_wanted_prop = in_param->length() - 1;
    int obj_prop = obj.prop_nb;

    for(int loop = 0; loop < nb_wanted_prop; loop++)
    {
        int lo;
        for(lo = 0; lo < obj_prop * 2; lo = lo + 2)
        {
            if(TG_strcasecmp((*in_param)[loop + 1], (*data_list)[obj.props_idx[lo]]) == 0)
            {
                int old_ret_length = ret_length;
                int nb_elt = obj.props_idx[lo + 1];

                ret_length = ret_length + 2 + nb_elt;

                ret_obj_prop.length(ret_length);
                ret_obj_prop[old_ret_length] = Tango::string_dup((*in_param)[loop + 1]);
                ret_obj_prop[old_ret_length + 1] = Tango::string_dup((*data_list)[obj.props_idx[lo] + 1]);

                for(int k = 0; k < nb_elt; k++)
                {
                    ret_obj_prop[old_ret_length + 2 + k] = Tango::string_dup((*data_list)[obj.props_idx[lo] + 2 + k]);
                }
                found_prop++;
                break;
            }
        }
        if(lo >= (obj_prop * 2))
        {
            int old_length = ret_length;
            ret_length = ret_length + 2;
            if(dev_prop)
            {
                ret_length++;
            }

            ret_obj_prop.length(ret_length);
            ret_obj_prop[old_length] = Tango::string_dup((*in_param)[loop + 1]);
            ret_obj_prop[old_length + 1] = Tango::string_dup("0");
            if(dev_prop)
            {
                ret_obj_prop[old_length + 2] = Tango::string_dup(" ");
            }
            found_prop++;
        }
    }
    std::snprintf(n_prop_str, sizeof(n_prop_str), "%d", found_prop);
    ret_obj_prop[1] = Tango::string_dup(n_prop_str);

    //    TANGO_LOG_DEBUG << "DbCache --> Data returned for a get_obj_property for object " << (*in_param)[0] <<
    //    std::endl; for (unsigned int ll=0;ll< ret_obj_prop.length();ll++)
    //        TANGO_LOG_DEBUG << "    DbCache --> Returned string = " << ret_obj_prop[ll] << std::endl;
}

//------------------------------------------------------------------------------------------------------------------
//
// method :
//      DbServerCache::get_dev_property()
//
// description :
//        This method returns to the caller the wanted device properties The returned data are the same than the one
//        returned by the classical API
//
// argument :
//         in :
//            - in_param : The device name followed by the wanted property names
//
// return :
//        This method returns a pointer to a DevVarStringArray initilised with the device properties
//
//-------------------------------------------------------------------------------------------------------------------

const DevVarStringArray *DbServerCache::get_dev_property(DevVarStringArray *in_param)
{
    int ret_length = 2;

    //
    // There is a special case for the dserver admin device
    //

    if(TG_strncasecmp((*in_param)[0], "dserver/", 8) == 0)
    {
        if(adm_dev_prop.first_idx == -1)
        {
            TangoSys_OMemStream o;
            o << "Device " << (*in_param)[0] << " not found in DB cache" << std::ends;

            TANGO_THROW_EXCEPTION(DB_DeviceNotFoundInCache, o.str());
        }
        ret_obj_prop.length(ret_length);
        ret_obj_prop[0] = Tango::string_dup((*in_param)[0]);

        get_obj_prop(in_param, adm_dev_prop, true);
    }
    else
    {
        int class_ind, dev_ind;
        int res = find_dev_att((*in_param)[0], class_ind, dev_ind);
        if(res == -1)
        {
            TangoSys_OMemStream o;
            o << "Device " << (*in_param)[0] << " not found in DB cache" << std::ends;

            TANGO_THROW_EXCEPTION(DB_DeviceNotFoundInCache, o.str());
        }
        else
        {
            ret_obj_prop.length(ret_length);
            ret_obj_prop[0] = Tango::string_dup((*in_param)[0]);

            get_obj_prop(in_param, classes_idx[class_ind].devs_idx[dev_ind].dev_prop, true);
        }
    }

    return &ret_obj_prop;
}

//-----------------------------------------------------------------------------
//
//  DbServerCache::get_dev_list()
//
//    This method returns to the list of devices for a specified class embedded
//  within the device server process
//    The returned data are the same than the one returned by
//  the classical API
//
//     in :    in_param : The device server name followed by the class name
//
//    This method returns a pointer to a DevVarStringArray initilised
//  with the device list
//-----------------------------------------------------------------------------

const DevVarStringArray *DbServerCache::get_dev_list(DevVarStringArray *in_param)
{
    int cl_idx = find_class((*in_param)[1]);
    if(cl_idx != -1)
    {
        ret_dev_list.length(classes_idx[cl_idx].dev_nb);
        for(int loop = 0; loop < classes_idx[cl_idx].dev_nb; loop++)
        {
            ret_dev_list[loop] = Tango::string_dup((*data_list)[classes_idx[cl_idx].dev_list.first_idx + 2 + loop]);
        }
    }
    else
    {
        ret_dev_list.length(0);
    }

    return &ret_dev_list;
}

//-----------------------------------------------------------------------------
//
//  DbServerCache::find_class()
//
//    This method returns the index of the class with name passed as parameter
//    into the array of classes info
//
//     in :    cl_name : The class name
//
//    This method returns the index in the classes info array of the wanted
//  class
//-----------------------------------------------------------------------------

int DbServerCache::find_class(DevString cl_name)
{
    for(int loop = 0; loop < class_nb; loop++)
    {
        if(TG_strcasecmp((*data_list)[classes_idx[loop].class_prop.first_idx], cl_name) == 0)
        {
            return loop;
        }
    }
    return -1;
}

//-----------------------------------------------------------------------------
//
//  DbServerCache::get_class_att_property()
//
//    This method returns to the caller the class attribute properties for the
//    class and attributes specified in the in parameters
//    The returned data are the same than the one returned by
//  the classical API
//
//     in :    in_param : The class name followed by the wanted
//                       attribute names
//
//    This method returns a pointer to a DevVarStringArray initilised
//  with the class attribute properties
//-----------------------------------------------------------------------------

const DevVarStringArray *DbServerCache::get_class_att_property(DevVarStringArray *in_param)
{
    char n_att_str[256];

    ret_obj_att_prop.length(2);
    ret_obj_att_prop[0] = Tango::string_dup((*in_param)[0]);

    int cl_idx = find_class((*in_param)[0]);
    if(cl_idx != -1)
    {
        int found_att = 0;

        //
        // The class is found
        //

        int wanted_att_nb = in_param->length() - 1;
        int class_att_nb = classes_idx[cl_idx].class_att_prop.att_nb;
        for(int loop = 0; loop < wanted_att_nb; loop++)
        {
            int ll;
            for(ll = 0; ll < class_att_nb; ll++)
            {
                if(TG_strcasecmp((*in_param)[loop + 1],
                                 (*data_list)[classes_idx[cl_idx].class_att_prop.atts_idx[ll]]) == 0)
                {
                    //
                    // The attribute is found, copy all its properties
                    //

                    int att_index = classes_idx[cl_idx].class_att_prop.atts_idx[ll];
                    int nb_prop = detail::parse_as<int>((*data_list)[att_index + 1].in());
                    int nb_elt = 0;
                    int nb_to_copy = 0;
                    int tmp_idx = att_index + 2;
                    nb_to_copy = 2;
                    for(int k = 0; k < nb_prop; k++)
                    {
                        nb_elt = detail::parse_as<int>((*data_list)[tmp_idx + 1].in());
                        tmp_idx = tmp_idx + nb_elt + 2;
                        nb_to_copy = nb_to_copy + 2 + nb_elt;
                    }

                    int old_length = ret_obj_att_prop.length();
                    ret_obj_att_prop.length(old_length + nb_to_copy);
                    for(int j = 0; j < nb_to_copy; j++)
                    {
                        ret_obj_att_prop[old_length + j] = Tango::string_dup((*data_list)[att_index + j]);
                    }
                    found_att++;
                    break;
                }
            }
            if(ll == class_att_nb)
            {
                found_att++;
                int old_length = ret_obj_att_prop.length();
                ret_obj_att_prop.length(old_length + 2);
                ret_obj_att_prop[old_length] = Tango::string_dup((*in_param)[loop + 1]);
                ret_obj_att_prop[old_length + 1] = Tango::string_dup("0");
            }
        }
        std::snprintf(n_att_str, sizeof(n_att_str), "%d", found_att);
        ret_obj_att_prop[1] = Tango::string_dup(n_att_str);
    }
    else
    {
        TangoSys_OMemStream o;
        o << "Class " << (*in_param)[0] << " not found in DB cache" << std::ends;

        TANGO_THROW_EXCEPTION(DB_ClassNotFoundInCache, o.str());
    }

    //    TANGO_LOG_DEBUG << "DbCache --> Returned data for a get_class_att_property for class " << (*in_param)[0] <<
    //    std::endl; for (unsigned int ll=0;ll< ret_obj_att_prop.length();ll++)
    //        TANGO_LOG_DEBUG << "    DbCache --> Returned object att prop = " << ret_obj_att_prop[ll] << std::endl;

    return &ret_obj_att_prop;
}

//-----------------------------------------------------------------------------
//
//  DbServerCache::get_dev_att_property()
//
//    This method returns to the caller the device attribute properties for the
//    device and attributes specified in the in parameters
//    The returned data are the same than the one returned by
//  the classical API
//
//     in :    in_param : The device name followed by the wanted
//                       attribute names
//
//    This method returns a pointer to a DevVarStringArray initialised
//  with the device attribute properties
//-----------------------------------------------------------------------------

const DevVarStringArray *DbServerCache::get_dev_att_property(DevVarStringArray *in_param)
{
    int found_att = 0;
    char n_att_str[256];

    ret_obj_att_prop.length(2);
    ret_obj_att_prop[0] = Tango::string_dup((*in_param)[0]);

    int class_ind, dev_ind;

    int ret_value = find_dev_att((*in_param)[0], class_ind, dev_ind);
    if(ret_value != -1)
    {
        int wanted_att_nb = in_param->length() - 1;
        int dev_att_nb = classes_idx[class_ind].devs_idx[dev_ind].dev_att_prop.att_nb;
        for(int loop = 0; loop < wanted_att_nb; loop++)
        {
            int ll;
            for(ll = 0; ll < dev_att_nb; ll++)
            {
                if(TG_strcasecmp((*in_param)[loop + 1],
                                 (*data_list)[classes_idx[class_ind].devs_idx[dev_ind].dev_att_prop.atts_idx[ll]]) == 0)
                {
                    int att_index = classes_idx[class_ind].devs_idx[dev_ind].dev_att_prop.atts_idx[ll];
                    int nb_prop = detail::parse_as<int>((*data_list)[att_index + 1].in());
                    int nb_elt = 0;
                    int nb_to_copy = 0;
                    int tmp_idx = att_index + 2;
                    nb_to_copy = 2;
                    for(int k = 0; k < nb_prop; k++)
                    {
                        nb_elt = detail::parse_as<int>((*data_list)[tmp_idx + 1].in());
                        tmp_idx = tmp_idx + nb_elt + 2;
                        nb_to_copy = nb_to_copy + 2 + nb_elt;
                    }

                    int old_length = ret_obj_att_prop.length();
                    ret_obj_att_prop.length(old_length + nb_to_copy);
                    for(int j = 0; j < nb_to_copy; j++)
                    {
                        ret_obj_att_prop[old_length + j] = Tango::string_dup((*data_list)[att_index + j]);
                    }
                    found_att++;
                    break;
                }
            }
            if(ll == dev_att_nb)
            {
                found_att++;
                int old_length = ret_obj_att_prop.length();

                ret_obj_att_prop.length(old_length + 2);
                ret_obj_att_prop[old_length] = Tango::string_dup((*in_param)[loop + 1]);
                ret_obj_att_prop[old_length + 1] = Tango::string_dup("0");
            }
        }
        std::snprintf(n_att_str, sizeof(n_att_str), "%d", found_att);
        ret_obj_att_prop[1] = Tango::string_dup(n_att_str);
    }
    else
    {
        if(TG_strncasecmp("dserver/", (*in_param)[0], 8) != 0)
        {
            TangoSys_OMemStream o;
            o << "Device " << (*in_param)[0] << " not found in DB cache" << std::ends;

            TANGO_THROW_EXCEPTION(DB_DeviceNotFoundInCache, o.str());
        }
        else
        {
            std::snprintf(n_att_str, sizeof(n_att_str), "%d", found_att);
            ret_obj_att_prop[1] = Tango::string_dup(n_att_str);
        }
    }

    //    TANGO_LOG_DEBUG << "DbCache --> Returned data for a get_dev_att_property for device " << (*in_param)[0] <<
    //    std::endl; for (unsigned int ll=0;ll< ret_obj_att_prop.length();ll++)
    //        TANGO_LOG_DEBUG << "    DbCache --> Returned object att prop = " << ret_obj_att_prop[ll] << std::endl;

    return &ret_obj_att_prop;
}

//-------------------------------------------------------------------------------------------------------------------
//
// method :
//      DbServerCache::find_dev_att()
//
// description :
//        This method sets the class and device index for the device with name passed in the in parameter. These indexes
//         are indexes in the local structure arrays
//
// argument :
//         in :
//            - dev_name : The device name
//          out :
//            - class_ind : The index of the data related to the device class
//            - dev_ind : The index of the data related to the device itself
//
// return :
//        This method returns 0 if everything OK or -1 if the device is not found
//
//-------------------------------------------------------------------------------------------------------------------

int DbServerCache::find_dev_att(DevString dev_name, int &class_ind, int &dev_ind)
{
    for(int loop = 0; loop < class_nb; loop++)
    {
        for(int ll = 0; ll < classes_idx[loop].dev_nb; ll++)
        {
            if(TG_strcasecmp((*data_list)[classes_idx[loop].dev_list.first_idx + 2 + ll], dev_name) == 0)
            {
                class_ind = loop;
                dev_ind = ll;
                return 0;
            }
        }
    }
    return -1;
}

//-------------------------------------------------------------------------------------------------------------------
//
//    method
//      DbServerCache::find_obj()
//
//    description :
//        This method sets the class and device index for the device with name passed in the in parameter.
//        These indexes are indexes in the local structure arrays
//
// argument
//         in :
//            - obj_name : The object name
//      out :
//            - obj_ind : The index of the data related to the object
//
// return :
//        This method returns 0 if everything OK or -1 if the device is not found
//
//--------------------------------------------------------------------------------------------------------------------

int DbServerCache::find_obj(DevString obj_name, int &obj_ind)
{
    if(ctrl_serv_prop.first_idx == -1)
    {
        return -1;
    }

    if(TG_strcasecmp((*data_list)[ctrl_serv_prop.first_idx], obj_name) == 0)
    {
        obj_ind = ctrl_serv_prop.first_idx;
        return 0;
    }

    return -1;
}

//------------------------------------------------------------------------------------------------------------------
//
// method :
//         DbServerCache::~DbServerCache()
//
// description :
//        destructor of the DbServerCache class
//
//------------------------------------------------------------------------------------------------------------------

DbServerCache::~DbServerCache()
{
    delete[] DServer_class_prop.props_idx;
    delete[] Default_prop.props_idx;
    delete[] adm_dev_prop.props_idx;
    delete[] ctrl_serv_prop.props_idx;

    for(int cl_loop = 0; cl_loop < class_nb; cl_loop++)
    {
        if(classes_idx[cl_loop].class_prop.props_idx != nullptr)
        {
            delete[] classes_idx[cl_loop].class_prop.props_idx;
        }
        if(classes_idx[cl_loop].class_att_prop.atts_idx != nullptr)
        {
            delete[] classes_idx[cl_loop].class_att_prop.atts_idx;
        }
        if(classes_idx[cl_loop].class_pipe_prop.atts_idx != nullptr)
        {
            delete[] classes_idx[cl_loop].class_pipe_prop.atts_idx;
        }

        for(int dev_loop = 0; dev_loop < classes_idx[cl_loop].dev_nb; dev_loop++)
        {
            if(classes_idx[cl_loop].devs_idx[dev_loop].dev_prop.props_idx != nullptr)
            {
                delete[] classes_idx[cl_loop].devs_idx[dev_loop].dev_prop.props_idx;
            }
            if(classes_idx[cl_loop].devs_idx[dev_loop].dev_att_prop.atts_idx != nullptr)
            {
                delete[] classes_idx[cl_loop].devs_idx[dev_loop].dev_att_prop.atts_idx;
            }
            if(classes_idx[cl_loop].devs_idx[dev_loop].dev_pipe_prop.atts_idx != nullptr)
            {
                delete[] classes_idx[cl_loop].devs_idx[dev_loop].dev_pipe_prop.atts_idx;
            }
        }
        delete[] classes_idx[cl_loop].devs_idx;
    }
    delete[] classes_idx;
}

//-----------------------------------------------------------------------------
//
//  DbServerCache::prop_indexes()
//
//    This method computes where is the last data related to the object
//    in the big array coming from the Db server
//
//     args (in) :    start : The index in the array of the first data related
//                        to the object
//                list : The string array coming from Db server
//
//  args (out) : stop : The index in the array of the last data related
//                        to the object
//                 obj : Structure where all object parameters must be stored
//-----------------------------------------------------------------------------

void DbServerCache::prop_indexes(int &start, int &stop, PropEltIdx &obj, const DevVarStringArray *list)
{
    start = stop + 1;
    int nb_prop = detail::parse_as<int>((*list)[start + 1].in());
    if(nb_prop == 0)
    {
        stop = start + 1;
        obj.last_idx = stop;
        obj.first_idx = start;
        obj.prop_nb = 0;
        obj.props_idx = nullptr;
        return;
    }
    stop = stop + 2;

    int id = 0;
    obj.props_idx = new int[nb_prop * 2];
    for(int loop = 0; loop < nb_prop; loop++)
    {
        obj.props_idx[id++] = stop + 1;
        int nb_elt = detail::parse_as<int>((*list)[stop + 2].in());
        obj.props_idx[id++] = nb_elt;
        stop = stop + nb_elt + 2;
    }

    obj.last_idx = stop;
    obj.first_idx = start;
    obj.prop_nb = nb_prop;
}

//-------------------------------------------------------------------------------------------------------------------
//
// method :
//      DbServerCache::prop_att_indexes()
//
// description :
//        This method computes where is the last data related to the object (for attribute related object) in the big
//        array coming from the Db server
//
// argument :
//         in :
//            - start : The index in the array of the first data related to the object
//            - list : The string array coming from Db server
//
//      out :
//            - stop : The index in the array of the last data related to the object
//            - obj : Structure where all object parameters must be stored
//
//-------------------------------------------------------------------------------------------------------------------

void DbServerCache::prop_att_indexes(int &start, int &stop, AttPropEltIdx &obj, const DevVarStringArray *list)
{
    start = stop + 1;
    int nb_att = detail::parse_as<int>((*list)[start + 1].in());
    if(nb_att == 0)
    {
        stop = start + 1;
        obj.last_idx = stop;
        obj.first_idx = start;
        obj.att_nb = 0;
        obj.atts_idx = nullptr;
        return;
    }

    int id = 0;
    obj.att_nb = nb_att;
    obj.atts_idx = new int[nb_att];
    stop = start + 2;

    for(int ll = 0; ll < nb_att; ll++)
    {
        obj.atts_idx[id++] = stop;
        int nb_prop = detail::parse_as<int>((*list)[stop + 1].in());
        stop = stop + 2;

        for(int loop = 0; loop < nb_prop; loop++)
        {
            int nb_elt = detail::parse_as<int>((*list)[stop + 1].in());
            stop = stop + nb_elt + 2;
        }
        if(ll == (nb_att - 1))
        {
            stop--;
        }
    }

    obj.last_idx = stop;
    obj.first_idx = start;
}

//------------------------------------------------------------------------------------------------------------------
//
// method :
//      DbServerCache::prop_pipe_indexes()
//
// description :
//        This method computes where is the last data related to the object (for attribute related object) in the big
//        array coming from the Db server
//
// argument :
//        in :
//            - start : The index in the array of the first data related to the object
//            - list : The string array coming from Db server
//        out :
//            - stop : The index in the array of the last data related to the object
//            - obj : Structure where all object parameters must be stored
//
//------------------------------------------------------------------------------------------------------------------

void DbServerCache::prop_pipe_indexes(int &start, int &stop, AttPropEltIdx &obj, const DevVarStringArray *list)
{
    start = stop + 1;
    int nb_att = detail::parse_as<int>((*list)[start + 1].in());
    if(nb_att == 0)
    {
        stop = start + 1;
        obj.last_idx = stop;
        obj.first_idx = start;
        obj.att_nb = 0;
        obj.atts_idx = nullptr;
        return;
    }

    int id = 0;
    obj.att_nb = nb_att;
    obj.atts_idx = new int[nb_att];
    stop = start + 2;

    for(int ll = 0; ll < nb_att; ll++)
    {
        obj.atts_idx[id++] = stop;
        int nb_prop = detail::parse_as<int>((*list)[stop + 1].in());
        stop = stop + 2;

        for(int loop = 0; loop < nb_prop; loop++)
        {
            int nb_elt = detail::parse_as<int>((*list)[stop + 1].in());
            stop = stop + nb_elt + 2;
        }
        if(ll == (nb_att - 1))
        {
            stop--;
        }
    }

    obj.last_idx = stop;
    obj.first_idx = start;
}

//-------------------------------------------------------------------------------------------------------------------
//
// method
//      DbServerCache::get_obj_property()
//
// description :
//        This method returns to the caller the wanted object properties. The returned data are the same than the one
//        returned by the classical API
//
// argument :
//         in :
//            - in_param : The object name followed by the wanted property names
//
// return :
//        This method returns a pointer to a DevVarStringArray initilised with the object properties
//
//-------------------------------------------------------------------------------------------------------------------

const DevVarStringArray *DbServerCache::get_obj_property(DevVarStringArray *in_param)
{
    int obj_ind;
    int res = find_obj((*in_param)[0], obj_ind);
    if(res == -1)
    {
        TangoSys_OMemStream o;
        o << "Object " << (*in_param)[0] << " not found in DB cache" << std::ends;

        TANGO_THROW_EXCEPTION(DB_DeviceNotFoundInCache, o.str());
    }
    else
    {
        int ret_length = 2;

        ret_obj_prop.length(ret_length);
        ret_obj_prop[0] = Tango::string_dup((*in_param)[0]);

        get_obj_prop(in_param, ctrl_serv_prop, true);
    }

    return &ret_obj_prop;
}

//------------------------------------------------------------------------------------------------------------------
//
// method :
//      DbServerCache::get_device_property_list()
//
// description :
//        This method returns to the caller the device property names. The returned data are the same than the one
//        returned by the classical API
//
// argument :
//         in :
//            - in_param : The device name followed by a wildcard
//
// return :
//    This method returns a pointer to a DevVarStringArray initilised with the device property names
//
//------------------------------------------------------------------------------------------------------------------

const DevVarStringArray *DbServerCache::get_device_property_list(DevVarStringArray *in_param)
{
    //
    // There is a special case for the dserver admin device
    //

    if(TG_strncasecmp((*in_param)[0], "dserver/", 8) == 0)
    {
        ret_prop_list.length(0);
        get_obj_prop_list(in_param, adm_dev_prop);
    }
    else
    {
        int class_ind, dev_ind;
        int res = find_dev_att((*in_param)[0], class_ind, dev_ind);
        if(res == -1)
        {
            TangoSys_OMemStream o;
            o << "Device " << (*in_param)[0] << " not found in DB cache" << std::ends;

            TANGO_THROW_EXCEPTION(DB_DeviceNotFoundInCache, o.str());
        }
        else
        {
            ret_prop_list.length(0);
            get_obj_prop_list(in_param, classes_idx[class_ind].devs_idx[dev_ind].dev_prop);
        }
    }

    return &ret_prop_list;
}

//-------------------------------------------------------------------------------------------------------------------
//
// method :
//      DbServerCache::get_obj_prop_list()
//
// description :
//        This method retrieves device property list for a device
//
// argument :
//         in :
//            - in_param : The device name followed by a wildcard
//            - obj : The device object in the cache
//
// retrun :
//        This method returns a pointer to a DevVarStringArray initialised with the device property names
//
//------------------------------------------------------------------------------------------------------------------

void DbServerCache::get_obj_prop_list(DevVarStringArray *in_param, PropEltIdx &obj)
{
    //
    // First analyse the wildcard
    //

    bool before = false, after = false, wildcard_used;
    std::string before_str;
    std::string after_str;
    std::string wildcard((*in_param)[1]);

    std::transform(wildcard.begin(), wildcard.end(), wildcard.begin(), ::tolower);

    std::string::size_type pos = wildcard.find('*');
    if(pos != std::string::npos)
    {
        wildcard_used = true;
        if(pos == 0)
        {
            if(wildcard.size() == 1)
            {
                before = false;
                after = false;
            }
            else
            {
                before = false;
                after = true;
                after_str = wildcard.substr(pos + 1);
            }
        }
        else
        {
            before = true;
            before_str = wildcard.substr(0, pos);
            if(pos == wildcard.size() - 1)
            {
                after = false;
            }
            else
            {
                after = true;
                after_str = wildcard.substr(pos + 1);
            }
        }
    }
    else
    {
        wildcard_used = false;
    }

    int ret_length = 1;
    int obj_prop = obj.prop_nb;

    int lo;
    std::string::size_type pos_after, pos_before;

    //
    // A loop on each prop.
    //

    for(lo = 0; lo < obj_prop * 2; lo = lo + 2)
    {
        //
        // Check according to the user wildcard, if we have to returned this property name
        // Note: The property name is case independant
        //

        bool store = false;
        if(wildcard_used)
        {
            if(!before)
            {
                if(!after)
                {
                    store = true;
                }
                else
                {
                    std::string tmp_name((*data_list)[obj.props_idx[lo]]);
                    std::transform(tmp_name.begin(), tmp_name.end(), tmp_name.begin(), ::tolower);

                    pos_after = tmp_name.rfind(after_str);
                    if((pos_after != std::string::npos) && (pos_after == (tmp_name.size() - after_str.size())))
                    {
                        store = true;
                    }
                }
            }
            else
            {
                std::string tmp_name((*data_list)[obj.props_idx[lo]]);
                std::transform(tmp_name.begin(), tmp_name.end(), tmp_name.begin(), ::tolower);

                if(!after)
                {
                    pos_before = tmp_name.find(before_str);
                    if((pos_before != std::string::npos) && (pos_before == 0))
                    {
                        store = true;
                    }
                }
                else
                {
                    pos_before = tmp_name.find(before_str);
                    pos_after = tmp_name.rfind(after_str);
                    if((pos_before != std::string::npos) && (pos_before == 0) && (pos_after != std::string::npos) &&
                       (pos_after == (tmp_name.size() - after_str.size())))
                    {
                        store = true;
                    }
                }
            }
        }
        else
        {
            std::string tmp_name((*data_list)[obj.props_idx[lo]]);
            std::transform(tmp_name.begin(), tmp_name.end(), tmp_name.begin(), ::tolower);

            if(tmp_name == wildcard)
            {
                store = true;
            }
        }

        //
        // Store the property name if decided
        //

        if(store)
        {
            ret_prop_list.length(ret_length);
            ret_prop_list[ret_length - 1] = Tango::string_dup((*data_list)[obj.props_idx[lo]]);
            ret_length = ret_length + 1;
        }
    }
}

//-------------------------------------------------------------------------------------------------------------------
//
// method :
//      DbServerCache::import_tac_dev()
//
// description :
//        This method returns to the caller of the info necessary to import the TAC device.
//        The returned data are the same than the one returned by the classical API
//
// return :
//        This method returns a pointer to a DevVarLongStringArray initilised with the Tango access control import data
//
//-------------------------------------------------------------------------------------------------------------------

const DevVarLongStringArray *DbServerCache::import_tac_dev(const std::string &tac_dev)
{
    //
    // Throw exception if no info in cache
    //

    if(imp_tac.last_idx == -1 || imp_tac.first_idx >= (int) data_list->length())
    {
        TANGO_THROW_EXCEPTION(API_DatabaseCacheAccess, "No TAC device in Db cache");
    }

    //
    // Throw exception if the device in cache is not the requested one
    //

    if(tac_dev.size() != strlen((*data_list)[imp_tac.first_idx]))
    {
        TANGO_THROW_EXCEPTION(API_DatabaseCacheAccess, "Device not available from cache");
    }

    std::string local_tac_dev(tac_dev);
    std::string cache_tac_dev((*data_list)[imp_tac.first_idx]);

    std::transform(local_tac_dev.begin(), local_tac_dev.end(), local_tac_dev.begin(), ::tolower);
    std::transform(cache_tac_dev.begin(), cache_tac_dev.end(), cache_tac_dev.begin(), ::tolower);

    if(local_tac_dev != cache_tac_dev)
    {
        TANGO_THROW_EXCEPTION(API_DatabaseCacheAccess, "Device not available from cache");
    }

    //
    // Throw exception if the device is not found in DB
    //

    if(::strcmp((*data_list)[imp_tac.first_idx + 1], "Not Found") == 0)
    {
        TangoSys_OMemStream o;
        o << "Device " << tac_dev << " not defined in database" << std::ends;

        TANGO_THROW_EXCEPTION(DB_DeviceNotDefined, o.str());
    }

    //
    // Return cache data
    //

    imp_tac_data.lvalue.length(2);
    imp_tac_data.svalue.length(5);

    imp_tac_data.lvalue[0] = detail::parse_as<CORBA::Long>((*data_list)[imp_tac.first_idx + 5].in());
    imp_tac_data.lvalue[1] = detail::parse_as<CORBA::Long>((*data_list)[imp_tac.last_idx - 1].in());

    for(int loop = 0; loop < 5; loop++)
    {
        imp_tac_data.svalue[loop] = Tango::string_dup((*data_list)[imp_tac.first_idx + loop]);
    }

    return &imp_tac_data;
}

//-------------------------------------------------------------------------------------------------------------------
//
// method :
//      DbServerCache::get_class_pipe_property()
//
// description :
//        This method returns to the caller the class pipe properties for the class and pipes specified in
//         the in parameters. The returned data are the same than the one returned by the classical API
//
// argument
//         in :
//            - in_param : The class name followed by the wanted pipe names
//
// return :
//        This method returns a pointer to a DevVarStringArray initilised with the class pipe properties
//
//-------------------------------------------------------------------------------------------------------------------

const DevVarStringArray *DbServerCache::get_class_pipe_property(DevVarStringArray *in_param)
{
    //
    // Throw exception if stored procedure does not support pipe
    //

    if(proc_release < 109)
    {
        std::string mess("Your database stored procedure is too old to support pipe. Please update to stored procedure "
                         "release 1.9 or more");
        TANGO_THROW_EXCEPTION(DB_TooOldStoredProc, mess);
    }

    int found_pipe = 0;
    char n_pipe_str[256];

    ret_obj_pipe_prop.length(2);
    ret_obj_pipe_prop[0] = Tango::string_dup((*in_param)[0]);

    int cl_idx = find_class((*in_param)[0]);
    if(cl_idx != -1)
    {
        //
        // The class is found
        //

        int wanted_pipe_nb = in_param->length() - 1;
        int class_pipe_nb = classes_idx[cl_idx].class_pipe_prop.att_nb;
        for(int loop = 0; loop < wanted_pipe_nb; loop++)
        {
            int ll;
            for(ll = 0; ll < class_pipe_nb; ll++)
            {
                if(TG_strcasecmp((*in_param)[loop + 1],
                                 (*data_list)[classes_idx[cl_idx].class_pipe_prop.atts_idx[ll]]) == 0)
                {
                    //
                    // The pipe is found, copy all its properties
                    //

                    int pipe_index = classes_idx[cl_idx].class_pipe_prop.atts_idx[ll];
                    int nb_prop = detail::parse_as<int>((*data_list)[pipe_index + 1].in());
                    int nb_elt = 0;
                    int nb_to_copy = 0;
                    int tmp_idx = pipe_index + 2;
                    nb_to_copy = 2;
                    for(int k = 0; k < nb_prop; k++)
                    {
                        nb_elt = detail::parse_as<int>((*data_list)[tmp_idx + 1].in());
                        tmp_idx = tmp_idx + nb_elt + 2;
                        nb_to_copy = nb_to_copy + 2 + nb_elt;
                    }

                    int old_length = ret_obj_pipe_prop.length();
                    ret_obj_pipe_prop.length(old_length + nb_to_copy);
                    for(int j = 0; j < nb_to_copy; j++)
                    {
                        ret_obj_pipe_prop[old_length + j] = Tango::string_dup((*data_list)[pipe_index + j]);
                    }
                    found_pipe++;
                    break;
                }
            }
            if(ll == class_pipe_nb)
            {
                found_pipe++;
                int old_length = ret_obj_pipe_prop.length();
                ret_obj_pipe_prop.length(old_length + 2);
                ret_obj_pipe_prop[old_length] = Tango::string_dup((*in_param)[loop + 1]);
                ret_obj_pipe_prop[old_length + 1] = Tango::string_dup("0");
            }
        }
        std::snprintf(n_pipe_str, sizeof(n_pipe_str), "%d", found_pipe);
        ret_obj_pipe_prop[1] = Tango::string_dup(n_pipe_str);
    }
    else
    {
        TangoSys_OMemStream o;
        o << "Class " << (*in_param)[0] << " not found in DB cache" << std::ends;

        TANGO_THROW_EXCEPTION(DB_ClassNotFoundInCache, o.str());
    }

    //    TANGO_LOG_DEBUG << "DbCache --> Returned data for a get_class_pipe_property for class " << (*in_param)[0] <<
    //    std::endl; for (unsigned int ll=0;ll< ret_obj_pipe_prop.length();ll++)
    //        TANGO_LOG_DEBUG << "    DbCache --> Returned object pipe prop = " << ret_obj_pipe_prop[ll] << std::endl;

    return &ret_obj_pipe_prop;
}

//-------------------------------------------------------------------------------------------------------------------
//
// method :
//      DbServerCache::get_dev_pipe_property()
//
// description :
//        This method returns to the caller the device pipe properties for the device and pipes specified in
//        the in parameters. The returned data are the same than the one returned by the classical API
//
// argument
//         in :
//            - in_param : The device name followed by the wanted pipe names
//
// return :
//        This method returns a pointer to a DevVarStringArray initialised with the device pipe properties
//
//------------------------------------------------------------------------------------------------------------------

const DevVarStringArray *DbServerCache::get_dev_pipe_property(DevVarStringArray *in_param)
{
    //
    // Throw exception if stored procedure does not support pipe
    //

    if(proc_release < 109)
    {
        std::string mess("Your database stored procedure is too old to support pipe. Please update to stored procedure "
                         "release 1.9 or more");
        TANGO_THROW_EXCEPTION(DB_TooOldStoredProc, mess);
    }

    int found_pipe = 0;
    char n_pipe_str[256];

    ret_obj_pipe_prop.length(2);
    ret_obj_pipe_prop[0] = Tango::string_dup((*in_param)[0]);

    int class_ind, dev_ind;

    int ret_value = find_dev_att((*in_param)[0], class_ind, dev_ind);
    if(ret_value != -1)
    {
        int wanted_pipe_nb = in_param->length() - 1;
        int dev_pipe_nb = classes_idx[class_ind].devs_idx[dev_ind].dev_pipe_prop.att_nb;
        for(int loop = 0; loop < wanted_pipe_nb; loop++)
        {
            int ll;
            for(ll = 0; ll < dev_pipe_nb; ll++)
            {
                if(TG_strcasecmp((*in_param)[loop + 1],
                                 (*data_list)[classes_idx[class_ind].devs_idx[dev_ind].dev_pipe_prop.atts_idx[ll]]) ==
                   0)
                {
                    int pipe_index = classes_idx[class_ind].devs_idx[dev_ind].dev_pipe_prop.atts_idx[ll];
                    int nb_prop = detail::parse_as<int>((*data_list)[pipe_index + 1].in());
                    int nb_elt = 0;
                    int nb_to_copy = 0;
                    int tmp_idx = pipe_index + 2;
                    nb_to_copy = 2;
                    for(int k = 0; k < nb_prop; k++)
                    {
                        nb_elt = detail::parse_as<int>((*data_list)[tmp_idx + 1].in());
                        tmp_idx = tmp_idx + nb_elt + 2;
                        nb_to_copy = nb_to_copy + 2 + nb_elt;
                    }

                    int old_length = ret_obj_pipe_prop.length();
                    ret_obj_pipe_prop.length(old_length + nb_to_copy);
                    for(int j = 0; j < nb_to_copy; j++)
                    {
                        ret_obj_pipe_prop[old_length + j] = Tango::string_dup((*data_list)[pipe_index + j]);
                    }
                    found_pipe++;
                    break;
                }
            }
            if(ll == dev_pipe_nb)
            {
                found_pipe++;
                int old_length = ret_obj_pipe_prop.length();

                ret_obj_pipe_prop.length(old_length + 2);
                ret_obj_pipe_prop[old_length] = Tango::string_dup((*in_param)[loop + 1]);
                ret_obj_pipe_prop[old_length + 1] = Tango::string_dup("0");
            }
        }
        std::snprintf(n_pipe_str, sizeof(n_pipe_str), "%d", found_pipe);
        ret_obj_pipe_prop[1] = Tango::string_dup(n_pipe_str);
    }
    else
    {
        if(TG_strncasecmp("dserver/", (*in_param)[0], 8) != 0)
        {
            TangoSys_OMemStream o;
            o << "Device " << (*in_param)[0] << " not found in DB cache" << std::ends;

            TANGO_THROW_EXCEPTION(DB_DeviceNotFoundInCache, o.str());
        }
        else
        {
            std::snprintf(n_pipe_str, sizeof(n_pipe_str), "%d", found_pipe);
            ret_obj_pipe_prop[1] = Tango::string_dup(n_pipe_str);
        }
    }

    //    TANGO_LOG_DEBUG << "DbCache --> Returned data for a get_dev_pipe_property for device " << (*in_param)[0] <<
    //    std::endl; for (unsigned int ll=0;ll< ret_obj_pipe_prop.length();ll++)
    //        TANGO_LOG_DEBUG << "    DbCache --> Returned object pipe prop = " << ret_obj_pipe_prop[ll] << std::endl;

    return &ret_obj_pipe_prop;
}

} // namespace Tango
