/*

 * GenericDataTransferProtocol.java

 *

 * Created on 31 de diciembre de 2003, 12:10

 */



package q02b.exe.arquitectura.webCommon.utils.gdtp;



//import java.io.IOException;

import java.util.HashMap;

import java.util.Map;



import q02b.exe.arquitectura.webCommon.utils.Q02bCommonUtils;

import q02b.exe.arquitectura.webCommon.utils.Q02bConstantes;

import q02b.exe.arquitectura.utils.Q02bEncoder;



/**

 * Esta clase se encarga de gestionar datos en formato String (serializado). Los datos serializados tienen el siguiente formato:<br>

 * (?GDTP_DATA=)?(?NOMBRE_GRUPO&(PROPIEDAD1=VALOR1)+)+<BR>

 * Por ejemplo:<br>

 * ?ERROR&ID_ERROR=4748&ID_SEVERIDAD=3?DATA&NOMBRE=TXOMIN&APELLIDO=DEL%20REGATO<BR>

 * ?GDTP_DATA=%3FERROR%26ID_ERROR%3D4748%26ID_SEVERIDAD%3D3%3FDATA%26NOMBRE%3DTXOMIN%26APELLIDO%3DDEL%2520REGATO<BR>

 * El valor de GDTP_DATA va escapado.

 * <p>

 * Los separadores de grupo, propiedad y valor de propiedad son configurables a través de las siguientes propiedades del grupo ARQ:

 * <ul>

 * <li>gdtp.separator.property</li>

 * <li>gdtp.separator.groups</li>

 * <li>gdtp.separator.propertyValue</li>

 * </ul>

 * </p>

 * Ojo, estas propiedades no se recargan en caliente...

 * @author  SERGIO.NAVARRO

 */

public class Q02bGenericDataTransferProtocol {

    /** Separador de propiedades de un grupo*/

    private static String PROPERTY_SEPARATOR= new String(new byte[]{0x02});//"&";

    /** Separador de grupos.*/

    private static String PROPERTY_GROUPS_SEPARATOR=new String(new byte[]{0x03});//"?";

    

    private static String PROPERTY_VALUE_SEPARATOR=new String(new byte[]{0x04});//"=";

    

    

    /** Mapa con clave=nombre de grupo y valor=Mapa con las propiedades correspondientes a ese grupo*/

    protected java.util.HashMap groupsProperties = new java.util.HashMap();

    /** Mapa con clave=nombre de grupo y valor=List con los nombres de las propiedades correspondientes a ese grupo*/

    protected java.util.HashMap groupsKeyList = new java.util.HashMap();

    /** List con los nombres de los grupos.*/

    protected java.util.ArrayList groups = new java.util.ArrayList();

    

    protected static final String DEFAULT_ENCODING = "ISO-8859-1";

	protected static final String NULL_STRING = "null";



    static{

        //String aux = Config.getProperty(Config.ARQ_GROUP,"gdtp.separator.property");

    	String aux = null;//"##prop##";

        if(aux!=null){

            aux = aux.trim();

            if(aux.length()>0){

                PROPERTY_SEPARATOR = aux;
            }
        }

        

        //aux = Config.getProperty(Config.ARQ_GROUP,"gdtp.separator.groups");

        aux = null;//"##group##";

        if(aux!=null){

            aux = aux.trim();

            if(aux.length()>0){

                PROPERTY_GROUPS_SEPARATOR = aux;
            }
        }

      

        //aux = Config.getProperty(Config.ARQ_GROUP,"gdtp.separator.propertyValue");

        aux = null;//"##propVal##";

        if(aux!=null){

            aux = aux.trim();

            if(aux.length()>0){

                PROPERTY_VALUE_SEPARATOR = aux;
            }
        }

    }

    

    

    /** Crea una nueva instancia de GenericDataTransferProtocol */

    public Q02bGenericDataTransferProtocol() {

    }

    

    

    /** Añade una propiedad a un grupo. Si el grupo no existe, se crea.

     * @param group Nombre del grupo

     * @param key Nombre de la propiedad

     * @param value Valor para la propiedad

     */

    public void addProperty(String group,String key,String value){

        addGroup(group);

        

        ((java.util.List)groupsKeyList.get(group)).add(key);

        ((java.util.Map)groupsProperties.get(group)).put(key,value);

    }

    

    /** Añade un Mapa con propiedades a un grupo.

     * @param group Nombre del grupo

     * @param data Mapa con pares clave=propiedad.

     */

    public void addProperties(String group,java.util.Map data){

        addGroup(group);

        

        ((java.util.List)groupsKeyList.get(group)).addAll(data.keySet());

        ((java.util.Map)groupsProperties.get(group)).putAll(data);

    }

    

    /** Parsea un String que contenga la definición completa de un grupo.

     * @param data String con la información correspondiente a un grupo

     * @param object Objeto GenericDataTransferProtocol en el que insertar el grupo parseado.

     */

    protected static void parseGroup(String data,Q02bGenericDataTransferProtocol object){

        object.parseGroup(data);

    }

    

    

    

    /** Parsea un String que contenga la definición completa de un grupo.

     * @param data String con la información correspondiente a un grupo

     */

    protected void parseGroup(String s){

        

        

        if(s.trim().length()==0){ return;}

        

        String group = null;

        String key = null;

        String value = null;

        java.util.List keys = null;

        java.util.Map data = null;

        int pos = 0;

        String aux = null;

        

        

        //String[] st = CommonUtils.split(s,PROPERTY_SEPARATOR);

        String[] st = Q02bCommonUtils.split(s,PROPERTY_SEPARATOR);

        

        for (int i = 0; i < st.length; i++) {

            if(i==0){//Lo primero que se encuentra es el nombre del grupo.

	            group = st[i];

	            addGroup(group);

	            keys = (java.util.List)groupsKeyList.get(group);

	            data = (java.util.Map)groupsProperties.get(group);

            }else{//Luego vienen las propiedades

                aux = st[i];

                pos = aux.indexOf(PROPERTY_VALUE_SEPARATOR);

                

                if(pos>=0){

                    key = aux.substring(0,pos);

                    value = aux.substring(pos+PROPERTY_VALUE_SEPARATOR.length());

                    

                    if(Q02bConstantes.ARQ_DB_NULL.equals(value)){

                        value = null;

                    }

                    

                    keys.add(key);

                    data.put(key,value);

                }

            }

            

            

        }

        

        

        

       

        

        

    }

    

    

    /** Parsea un String, y devuelve un objeto de tipo GenericDataTransferProtocol con los

     * datos parseados

     * @param input String de donde leer los datos

     * @throws IOException Si se produce un error de lectura

     * @return GenericDataTransferProtocol con los

     * datos parseados

     */

    public static Q02bGenericDataTransferProtocol parse(String input)throws java.io.IOException{

        Q02bGenericDataTransferProtocol object = new Q02bGenericDataTransferProtocol();

        object.parseNS(input);

        return object;

    }

    

    /** Parsea un String, y devuelve un objeto de tipo GenericDataTransferProtocol con los

     * datos parseados

     * @param input InputStream de donde leer los datos

     * @throws IOException Si se produce un error de lectura

     * @return GenericDataTransferProtocol con los

     * datos parseados

     */

    public static Q02bGenericDataTransferProtocol parse(java.io.InputStream input)throws java.io.IOException{

        return parse(new java.io.InputStreamReader(input));

    }

    

    /** Parsea un String, y devuelve un objeto de tipo GenericDataTransferProtocol con los

     * datos parseados

     * @param reader Reader de donde leer los datos

     * @throws IOException Si se produce un error de lectura

     * @return GenericDataTransferProtocol con los datos parseados

     */

    public static Q02bGenericDataTransferProtocol parse(java.io.Reader reader)throws java.io.IOException{

        StringBuffer sb = new StringBuffer();

        int c = 0;

        

        while( (c=reader.read())>=0 ){

            sb.append((char)c);

        }

        

        reader.close();

        

        return parse(sb.toString());

    }

    

    

    /** Parsea un String cogiendo los grupos y propiedades correspondientes a los mismos.

     * @param reader Reader de donde leer los datos

     * @throws IOException Si se produce un error de lectura

     */

    protected void parseNS(String data){

        int pos = data.indexOf("GDTP_DATA=");

        if(pos>=0){

            data = Q02bEncoder.unescape( data.substring(pos+10),DEFAULT_ENCODING );

        }

        

        String[] st = Q02bCommonUtils.split(data,PROPERTY_GROUPS_SEPARATOR);

        for (int i = 0; i < st.length; i++) {

            parseGroup(st[i]);

        }

    }

    

    /**

     * Añade un grupo

     * @param group Nombre del grupo a añadir

     */

    public void addGroup(String group){

        if(!groups.contains(group)){

            groups.add(group);

            groupsKeyList.put(group,new java.util.ArrayList());

            groupsProperties.put(group , new java.util.HashMap());

        }

    }

    

    /** Serializa el objeto

     * @return Objeto serializado

     */

    public String toString(){

        try{

            return serialize();

        }catch(Exception ex){

            return ex.getLocalizedMessage();

        }

    }

    

    /** Serializa el objeto

     * @param out OutputStream donde serializar

     * @throws IOException Si se produce un error de lectura

     */

    public void serialize(java.io.OutputStream out)throws java.io.IOException{

        serialize( new java.io.OutputStreamWriter(out) );

    }

    

    /** Serializa el objeto

     * @param out Writer donde serializar

     * @throws IOException Si se produce un error de lectura

     */

    public void serialize(java.io.Writer out)throws java.io.IOException{

        out.write( serialize() );

    }

    

    /** Serializa los datos correspondientes al objeto.

     * @return Objeto serializado

     * @throws IOException Si se produce un error de lectura

     */

    public String serialize(){

    	try{

            //monitor = com.jamonapi.MonitorFactory.start("gdtp.serialize");

            

            StringBuffer sb = new StringBuffer();

            

            String group = null;

            java.util.List keys = null;

            java.util.Map data = null;

            String key = null;

			Object value = null;

			

            int keysSize = 0;

            for(int i=0;i<groups.size();i++){

            	

                group = groups.get(i).toString();

                keys = (java.util.List)groupsKeyList.get(group);

                data = (java.util.Map)groupsProperties.get(group);

                sb.append(PROPERTY_GROUPS_SEPARATOR);

                sb.append(group);

            

				keysSize = keys.size();

                for(int j=0;j<keysSize;j++){

                    sb.append(PROPERTY_SEPARATOR);

                    

                    key = keys.get(j).toString();

                    value = data.get( key );



                    if(value==null){ value = NULL_STRING;}

                    

                    sb.append( key ).append(PROPERTY_VALUE_SEPARATOR).append(value);

                }

            }

            

            StringBuffer out = new StringBuffer("GDTP_DATA=");

            out.append(Q02bEncoder.escapeWBlanks( sb.toString() ,DEFAULT_ENCODING));

            

            return out.toString();

            

        }finally{

            //monitor.stop();

        }

    }

    

    /** Devuelve el valor de una propiedad, perteneciente a un grupo

     * @param group Nombre del grupo

     * @param key Nombre de la propiedad

     * @return Valor de la propiedad

     */

    public String getPropertyValue(String group,String key){

        try{

            java.util.Map values = (java.util.Map)groupsProperties.get(group);

            return (String)values.get(key);

        }catch(NullPointerException ex){

            return null;

        }

    }

    /** Elimina una propiedad, perteneciente a un grupo

     * @param group Nombre del grupo

     * @param key Nombre de la propiedad

     * 

     */

    public String removeProperty(String group,String key){

        try{

            java.util.Map values = (java.util.Map)groupsProperties.get(group);

            return (String)values.remove(key);

        }catch(NullPointerException ex){

            return null;

        }

    }    

    public void setPropertyValue(String group,String key,String value){

        try{

           ((Map)groupsProperties.get(group)).put(key,value);

        }catch(NullPointerException ex){

            

        }

      }   

    /** Devuelve un mapa con las propiedades correspondientes a un grupo

     * @param group Nombre del grupo

     * @return Mapa con las propiedades correspondientes a un grupo

     */

    public java.util.Map getGroupPropertyMap(String group){

        return (java.util.Map)groupsProperties.get(group);

    }

    

    /** Devuelve una lista con los nombres de las propiedades correspondientes a un grupo

     * @param group Nombre del grupo

     * @return Lista con los nombres de las propiedades correspondientes a un grupo

     */

    public java.util.List getGroupPropertyNames(String group){

        return (java.util.List)groupsKeyList.get(group);

    }

    

    /**

     * Devuelve un Map con TODAS las propiedades de TODOS los grupos.

     * @return Mapa con TODAS las propiedades de TODOS los grupos.

     */

    public java.util.Map getAllProperties(){

        java.util.Map map = new java.util.HashMap();

        

        for(int i=0;i<groups.size();i++){

            map.putAll( (java.util.Map)groupsProperties.get(groups.get(i)) );

        }

        

        return map;

    }

    

    /**

     * Libera todos los recursos, vaciando los List y los Map.

     */

    public void clear(){

        groupsProperties.clear();

        groupsKeyList.clear();

        groups.clear();

    }

    



    /**

     * Añade los grupos y propiedades de gdtp recibido como parámetro.

     * @param gdtp GDTP del cual vamos a añadir todo su contenido

     */

    public void merge(Q02bGenericDataTransferProtocol gdtp){

        String[] grps = (String[])gdtp.groups.toArray(new String[gdtp.groups.size()]);

        

        

        for (int i = 0; i < grps.length; i++) {

            this.addGroup(grps[i]);

            this.addProperties(grps[i],cloneMap(gdtp.getGroupPropertyMap(grps[i])));

        }

        

    }

    

    /**

     * Genera un mapa con el contenido del mapa recibido

     * @param map Mapa a clonar

     * @return Nuevo mapa

     */

    public static final Map cloneMap(Map map){

        HashMap newMap = new HashMap();

        

        Object[] keys = map.keySet().toArray();

        for (int i = 0; i < keys.length; i++) {

            newMap.put(keys[i],map.get(keys[i]));

        }

        

        return newMap;

    }



    /**

     * Devuelve un objeto GDTP con el mismo contenido que el actual

     * @return Objeto GDTP con el mismo contenido que el actual

     */

    public Q02bGenericDataTransferProtocol cloneGDTP(){

        Q02bGenericDataTransferProtocol gdtp = new Q02bGenericDataTransferProtocol();

        

        gdtp.merge(this);

        return gdtp;

    }

    

    

    public static void main(String[] args) throws Throwable{

        Q02bGenericDataTransferProtocol gdtp = new Q02bGenericDataTransferProtocol();

        gdtp.addGroup("PEPE");

        gdtp.addProperty("PEPE","A","B");

        

        //Q02bGenericDataTransferProtocol newGdtp = gdtp.cloneGDTP();

        

        

        //GenericDataTransferProtocol gdtp = GenericDataTransferProtocol.parse(new java.io.FileInputStream(args[0]));

        

        

        

    }

}

