Tutorial do HierarchicalMap

HierarchicalMap é uma interface. Assim, qualquer um pode implementá-la. Este tutorial é baseado em BasicHierarchicalmap, uma implementação referência em Java da interfae HierarchicalMap.

Este tutorial abrange os seguintes assuntos:

    Nível Básico
  1. O Primeiro Contato
  2. Criação de Estruturas
  3. Recuperação dos Dados
  4. Interação com Coleções
  5. Reestruturação do Mapa

  6. Nível Intermediário
  7. Trabalhando com Stream
  8. Trabalhando com XML
  9. Trabalhando com XML Grande
  10. Acessando Banco de Dados
  11. Acessando Preferências

  12. Nível Avançado
  13. Preenchimento de Template (inglês)
  14. Interoperabilidade com .NET (inglês)
  15. Runtime schema validation (inglês)

(O recurso de Javascript precisa estar habilitado, no seu navegador, para permitir a visualização dos códigos utilizados nos exemplos deste tutorial)

Nível Basico

1. O Primeiro Contato

A interface HierarchicalMap é composta por três conjutos principais de métodos e alguns métodos auxiliares:

  • Operações de recuperação: get(key)
  • Operações de alteração de estrutura: put(key, value), add(key, value), addAll(key, collection), remove(key) e removeAll(key)
  • Operações sobre coleções: values(), keySet(), entrySet() e getAll(key)
  • Métodos auxiliares: newInstance(), size(), add(key) e addAll(HierarchicalMap)
  • Segue abaixo o nosso primeiro código de exemplo (clássico):

    HierarchicalMap map = new BasicHierarchicalMap();
    map.put("Saudação", "Hello World!");
    	 
    System.out.println(map.get("Saudação"));

    Como pode ser visto, HierarchicalMap se comporta exatamente como um HashMap quando se utiliza uma chave simples.

    2. Criação de Estruturas

    Agora, iremos criar uma estrutura um pouco mais complexa:

    Nesta estrutura, cada círculo representa um nó (que é uma instância da HierarchicalMap), e cada retângulo representa uma folha, que pode ser qualquer objeto (como por exemplo uma instância de java.lang.String). Um nó pode conter outros nós como no caso do "Pedido" e do "Endereço", ou uma folha como no caso do "tipo", "Cidade", "Estado" e do "CEP".

    O trecho de código abaixo cria uma estrutra de dados representando o diagrama acima:

    //criar uma HierachicalMap contendo o primeiro endereço
    HierarchicalMap endereco1 = new BasicHierarchicalMap();
    endereco1.put("tipo", "Fatura");
    endereco1.put("Cidade", "Sta Clara");
    endereco1.put("Estado", "CA");
    endereco1.put("CEP", "95054");
    
    //adiciona o endereço criado na hmap
    HierarchicalMap hmap = new BasicHierarchicalMap();
    hmap.add("Pedido/Endereço", endereco1);
    //cria uma outra HierachicalMap contendo o 
    //segundo endereço
    HierarchicalMap endereco2 = new BasicHierarchicalMap();
    endereco2.put("tipo", "Entrega");
    endereco2.put("Cidade", "São Paulo");
    endereco2.put("Estado", "SP");
    endereco2.put("CEP", "04717-004");
    
    //adiciona o segundo endereço na hmap
    hmap.add("Pedido/Endereço", endereco2);

    O método "add" possui uma outra assinatura que recebe somente a chave, sem o valor. Neste caso, uma nova instância da HierarchicalMap é criada e adicionada sob a chave. Desta forma, o segundo endereço poderia ser adicionado de forma diferente como no exemplo abaixo:

    //cria uma outra HierachicalMap contendo o 
    //segundo endereço
    HierarchicalMap endereco2 = hmap.add("Pedido/Endereço");
    endereco2.put("tipo", "Entrega");
    endereco2.put("Cidade", "São Paulo");
    endereco2.put("Estado", "SP");
    endereco2.put("CEP", "04717-004");
    3. Recuperação dos Dados

    Uma vez criada a estrutra, os dados podem ser recuperadas:

    System.out.println(hmap.get("Pedido/Endereço/Cidade"));
    System.out.println(address2.get("Estado"));

    O código da Listagem acima resulta na seguinte saída:

    Sta Clara
    SP
    

    Note que a operação get recupera somente um item de cada vez, e no caso de possuir mais itens, o primeiro dado inserido é retornado. Assim, mesmo havendo duas cidades armazenadas na estrutura, a primeira cidade, no caso "Sta Clara", foi obtida no exemplo anterior. Por outro lado, o valor retornado para o estado foi "SP", pois a operação foi efetuada sobre o nó "endereco2". Para obter todos os itens de uma mesma chave, deve-se utilizar a operação getAll:

    //exemplo utilizando getAll que retorna uma Collection
    //contendo objetos que possuem a mesma chave
    java.util.Collection val 
                    = hmap.getAll("Pedido/Endereço/Cidade");
    
    //get the iterator to printout all the objects returned
    java.util.Iterator itr = val.iterator();
    while(itr.hasNext()) {            
        System.out.println(itr.next());
    }

    O resultado da execução do código acima será:

    Sta Clara
    São Paulo
    
    4. Interação com Coleções

    A estrutura de dados pode ser percorrida através de Iterator:

    //primeiro, obtém todos os endereços
    java.util.Collection <HierarchicalMap> enderecos = 
        hmap.getAll("Pedido/Endereço");
    
    // para cada endereco em enderecos
    for( HierarchicalMap endereco : enderecos ) {
        System.out.println("Endereço");
        // obter o entrySet para percorrer os nós filhos, 
        // ou seja: Cidade, Estado e CEP
        java.util.Set <Map.Entry> itens = endereco.entrySet();
        
        //imprimir o par chave valor para cada item 
        //de endereco
        for(Map.Entry item : itens) {
            System.out.println(item.getKey() + " : " +
                    item.getValue());
            
        }
        System.out.println();
    }
    }

    A saída produzida será:

    Address
    type : Billing
    City : Sta Clara
    State : CA
    ZIP code : 95054
    
    Address
    type : Shipping
    City : São Paulo
    State : SP
    ZIP code : 04717-004
    
    5. Reestruturação do Mapa

    Apesar da estrutura utilizada até então estar em conformidade com a recomendação da W3C, não conseguimos extrair a vantagem da HierarchicalMap. Informações organizadas desta maneira, não permite acessar aos dados internos de forma direta (ex. recuperar o estado do endereço de entrega). Este acesso direto, pode ser obtido através de uma pequena reestruturação na HierarchicalMap. Assim, podemos implementar um método como o que está descrito a seguir:

    public static void denormalize(HierarchicalMap map,
                        String field, String keyField) {
        //primeiro, remove todos os campos e 
        //substituir por uma mapa vazia
        Collection col = map.removeAll(field);
        HierarchicalMap newMap = map.add(field);
    
        //iterar através dos campos removidos
        Iterator itr = col.iterator();
        while(itr.hasNext()) {
            //para cada objeto removido, verificar se é um nó
            //(i.e. instance of HierarchicalMap)
            Object o = itr.next();
            if(o instanceof HierarchicalMap) {
                //se sim, tenta recuperar a chave 
                //onde o nó será adicionado
                Object key = ((HierarchicalMap)o)
                             .get(keyField);
                if(key != null) {
                    newMap.add(key.toString(), o);
                }
            }
        }
    }

    Com o métdod acima, podemos transformar o mapa original para uma estutura como abaixo:

    Agora, podemos reucperar de forma mais direta:

    //executar o método denormalize para reorganizar a
    //estrutura da Figura 7 para Figura 8
    denormalize(map,"Pedido/Endereço","tipo");
    		
    //now fetch the desiered information directly
    System.out.print("Estado para Entrega: ");
    System.out.println(map
                     .get("Pedido/Endereço/Entrega/Estado"));
    System.out.print("Estado para a Faturamento: ");
    System.out.println(map
                     .get("Pedido/Endereço/Fatura/Estado"));

    O resultado será:

    Estado para Entrega: SP
    Estado para a Faturamento: CA

    O método abaixo consegue reoganizar o mapa, de volta para estrutura original:

    public static void normalize(HierarchicalMap map,
                        String field, String keyField) {
       //primeiro, remove todos os campos
       Collection col = map.removeAll(field);
    
       Iterator itr = col.iterator();
       while(itr.hasNext()) {
          //para cada objeto removido, validar se é um nó
          //(i.e. instance of HierarchicalMap)
          Object o = itr.next();
          if(o instanceof HierarchicalMap) {
             //Então, criar um novo mapa com cada item 
             //do sub-nó.
             //Adicionar o novo mapa sob a chave passada como 
             //parâmetro
             Iterator itr2 = 
                 ((HierarchicalMap)o).entrySet().iterator();
             while(itr2.hasNext()) {
                Entry e = (Entry)itr2.next();
                if(e.getValue() instanceof HierarchicalMap) {
                   HierarchicalMap newMap = 
                        (HierarchicalMap)e.getValue();
                   //note que foi utilizado o put no lugar 
                   //do add para garantir a unicidade do 
                   //campo chave
                   newMap.put(keyField, e.getKey());
                   map.add(field, newMap);
                }
             }
          }
       }
    }

    Realmente, isto é tudo sobre HierarchicalMap. Como pode ver é tão simples como isso.

    Porém, podemos aplicar esta interface em soluções mais interessantes e algumas vezes mais complexas.

    Continua para Nível Intermediário