Mostrando postagens com marcador PROGRAMAÇÃO. Mostrar todas as postagens

quinta-feira, 1 de setembro de 2016

[ADVPL] Desenvolvendo classes

Fala galera!

Uma das coisa mais interessantes de se aprender durante o desenvolvimento de algumas rotinas em ADVPL, é o uso de classes. Confesso que para mim isso é um pouco recente, mas estou aplicando sempre que possível em meus fontes.




Com o uso de classes, você tem a possibilidade de reduzir as linhas código do seu fonte, evitar repetição de funções e centralizar uma atividade específica em um único arquivo.
Para ficar melhor exemplificado, vou passar à vocês uma forma que utilizei para simplificar/facilitar o processo de envio de emails (worflow) do Protheus. Anteriormente em nossa base havia um fonte onde possuía uma função de usuário que era chamada sempre que fosse necessário enviar os e-mails, ou seja,  em meio ao meu código fonte - caso eu precisasse enviar e-mail - chamaria a função desta forma:
user function envioMail(cMsg, cDe, cPara, cAssunto)

local cCc := ""
local cBcc := ""
local cAnexo := ""
local cTitulo := ""

cEnvMail := u_EnvMail(  cDe,;      //cDe - origem
   cPara,;    //cPara
   cCc,;    //cCc
   cBcc,;     //cBCC
   cAssunto,; //cTitulo
   cAssunto,; //cAssunto
   cAnexo,;   //cAnexo
   cMsg)    //cMsg

return

A função envioMail() é do fonte que estou desenvolvendo e para enviar o e-mail, preciso "chamar" a outra função (outro fonte/arquivo) chamado u_EnvMail() definindo todos os parâmetros.
Tudo bem! Desta forma é funcional, mas nosso objetivo é tornar o acesso a atividade de envio de e-mail através de uma classe, onde poderíamos - futuramente - trabalhar com objetos e configurar outras funções para nossa classe.

Vamos aos códigos!

Primeiramente vamos adicionar um novo arquivo ao projeto e salvá-lo com o nome ClassMail e adicionaremos o código abaixo:

class ClassMail

      //dados de conexão com o server
      data cServer    //Nome do servidor de envio de e-mail
      data cEmail     //Conta a ser utilizada no envio de e-mail
      data cEmailA    //Usuário para autenticação no servidor de e-mail
      data cPass      //Senha para autenticação no servidor de e-mail
      data lAuth      //Verifica se servidor necessita de autenticação
      data lResulConn //Flag de conexão com o servidor
      data lResulSend //Flag de sucesso de envio de e-mail
      data cError     //Mensagem de erro

      //variáveis para envio do email
      data cDe      //E-mail de origem
      data cPara    //E-mail de destino
      data cCc      //E-mail de cópia
      data cBcc     //E-mail de cópia oculta
      data cTitulo  //Título do e-mail
      data cAssunto //Assunto do e-mail
      data cAnexo   //Arquivo que será anexado
      data cMsg     //Mensagem/Conteúdo do e-mail

      //metodo construtor da classe com as variáveis que são enviadas por parâmetro
      method new(cDe, cPara, cCc, cBcc, cTitulo, cAssunto, cAnexo, cMsg) CONSTRUCTOR

      //método de validação dos dados recebidos
      method valDados()

      //método de validação de conexão com o servidor de envio de e-mail
      method valConn()

      //método responsável para efetuar o envio do e-mail
      method sendMail()

endClass
Nesse primeiro ponto, temos a definição das variáveis e métodos de nossa classe. Temos as variáveis responsáveis para efetuar, autorizar e obter o retorno de conexão com o servidor de e-mails e as variáveis que receberemos por parâmetro através do método construtor da nossa classe. depois temos os métodos da classe que utilizaremos durante a execução como validação de dados, conexão e o envio de e-mail.

Em nosso método construtor, vamos receber os dados e atribuir as variáveis da nossa classe. Observe que, quando for referenciar uma variável da classe deve-se utilizar o '::' na frente dela, ou seja, seria algo como ::cEmail. É importante também adicionar ao final do método o nome da classe.

Os métodos podem e devem obter um retorno, somente o tipo do conteúdo/variável que será retornado dependendo da sua aplicação. Veja que em nosso método construtor, não há necessidade retorno, então inserimos a palavra reservada self.

method new(cDe, cPara, cCc, cBcc, cTitulo, cAssunto, cAnexo, cMsg) class ClassMail

 ::cDe      := cDe
 ::cPara    := cPara
 ::cCc      := cCc
 ::cBcc     := cBcc
 ::cTitulo  := cTitulo
 ::cAssunto := cAssunto
 ::cAnexo   := cAnexo
 ::cMsg     := cMsg

return self

Agora vamos aplicar a validação dos dados principais de nossa classe. através do método valDados() Não é possível enviar um e-mail sem um e-mail de origem, destino e assunto - ou qualquer outro campo que você desejar.

method valDados() class ClassMail

local lRet := .T.

if empty(::cDe) .or. empty(::cPara) .or. empty(::cAssunto)
 msgAlert("Os campos 'De','Para' ou 'Assunto' devem ser preenchidos corretamente!","ERRO")
 lRet := .F.
endIf

return lRet

Precisamos agora do método responsável por efetuar a conexão com o servidor de e-mails e, para isso, vamos definir outro método nomeado valConn().

method valConn() class ClassMail

local lRet := .T.

::cServer := allTrim(getMv("MV_RELSERV")) //Nome do servidor de envio de e-mail
::cEmail  := allTrim(getMv("MV_RELACNT")) //Conta a ser utilizada no envio de e-mail
::cEmailA := allTrim(getMv("MV_RELAUSR")) //Usuário para autenticação no servidor de e-mail
::cPass   := allTrim(getMv("MV_RELAPSW")) //Senha para autenticação no servidor de e-mail
::lAuth   := getMv("MV_RELAUTH") //Verifica se servidor necessita de autenticação

if (empty(::cServer) .or. empty(::cEmail) .or. empty(::cEmailA) .or. empty(::cPass)) .and. ::valDados()
 msgAlert("Não foi possível obter os dados dos parâmetros de envio de e-mail do Protheus!","Erro envio de e-mail")
 lRet := .F.
else
 //Efetua a conexão com o servidor
 CONNECT SMTP SERVER ::cServer ACCOUNT ::cEmail PASSWORD ::cPass RESULT ::lResulConn

 //se necessita a autenticação do servidor
 if ::lAuth
  lOk := MailAuth(::cEmailA, ::cPass)
  if !lOk
   lOk := QAGetMail()
  endIf
 endIf

 if !::lResulConn
  get MAIL ERROR ::cError
  msgAlert("Falha na conexão: " + ::cError,"Erro - Problema de conexão")
  lRet := .F.
 endIf
endIf

return lRet

Depois desses métodos desenvolvidos precisamos fazer o envio do e-mail. Então o método sendMail() fará isso para nós.

method sendMail() class ClassMail

//se as validações (conexão e dados) estiverem ok. Efetua o envio do e-mail
if ::valConn() .and. ::valDados()

 SEND MAIL from ::cDe;
  to         ::cPara;
  CC         ::cCc;
  BCC        ::cBcc;
  SUBJECT    ::cAssunto;
  BODY       ::cMsg;
  ATTACHMENT allTrim(::cAnexo);
  RESULT     ::lResulSend

 get MAIL ERROR ::cError

 if !::lResulSend
  msgAlert("Falha no envio do e-mail: " + ::cError,"Erro - E-mail não enviado")
 endif

 DISCONNECT SMTP SERVER
else
 msgInfo("Email não enviado!")
endIf

return self

Bom, com o intuito de evitar tentativa de conexão desnecessária, condicionaremos essa ação ao retorno dos métodos valConn() e valDados() - Note que, assim como as variáveis, os métodos devem ser tratados dentro da classe com '::'.

Assim que testarmos o nosso fonte e compilarmos, podemos substituir a chamada na função original onde precisamo enviar o email. Então ele ficará modificado da seguinte forma:

user function envioMail(cMsg, cDe, cPara, cAssunto)

local cCc := ""
local cBcc := ""
local cAnexo := ""
local cTitulo := ""

objMail := ClassMail():new(cDe, cPara, cCc, cBcc, cTitulo, cAssunto, cAnexo, cMsg)
objMail:sendMail()

return

Em nossa função de usuário teremos um objeto - objMail - que será uma instancia da nossa classe ClassMail() e passará em seu método construtor as informações para o envio do e-mail. Após isso, basta chamar o método sendMail(). 

Com o desenvolvimento de classes podemos incrementar nossas possibilidades de uso de funções e aplicações específicas do sistema, tornando mais fácil o desenvolvimento e manutenção do fonte. Claro que aqui foi dado um exemplo simples, mas é possível adicionar mais funcionalidades de acordo com o que precisar.

Até mais!

;)

segunda-feira, 6 de junho de 2016

GO GO! Congresso de TI

Pessoal, hoje é o primeiro dia do evento da 4ª Edição do Congresso de TI.
Segue abaixo a lista das transmissões de hoje:

GO GO! Congresso de TI

Todas as palestras de hoje ficarão liberadas das 07hrs da manhã até 23:59hrs - horário de Brasília.
Allan Romanato
 
André de Almeida

Carlos Ferreira

Christyano Malta

David Arty

Fillipe Cordeiro

Luis Felipe Fernandes

Marco Enes

Plínio Ventura


Palestras bastante diversificadas para todos os gostos rs
Importante que você avalie a transmissão das palestras. Dessa forma os idealizadores poderão estar sempre melhorando a qualidade do evento.

--------------------------------------------------------------------------
PS: Receba também o Link das Palestras do Congresso de TI no seu Smartphone:

1 - Instale o aplicativo TELEGRAM no seu celular caso ainda não tenha. 

2 - Após instalar, digite no seu Telegram por @congressodeti ou acesse o link: http://telegram.me/congressodeti

3 - Clique no ícone "Canal Congresso de TI" e depois em ENTRAR. Pronto, agora você já estará incluído no nosso canal do Telegram para receber todos os links de transmissões das palestras durante o evento.
--------------------------------------------------------------------------

Até a próxima!

sexta-feira, 3 de junho de 2016

GO GO! Técnicas de Invasão

Mais um evento super bacana online e totalmente grátis que você não pode perder ," Técnicas de Invasão em Cenários Reais".

O evento acontecerá dos dias 06 à 13 de Junho, e nesse treinamento você vai aprender técnicas que quebram os principais pilares de Segurança da Informação, como criminosos (crackers) comprometem Redes, Sistemas, Aplicações WEB Infraestruturas físicas.


Para se inscrever basta clicar nele link aqui, clicar no botão "Realizar inscrição", informar seu email e pronto! Você um email de confirmação e só esperar o dia do treinamento.

Bruno Fraga que é o idealizador do projeto, deixou esse vídeo com uma ferramenta muito legal pra já ter uma ideia do que o curso esta preparando.




Pessoal não perca essa oportunidades, muito se fala sobre segurança da informação, hacker e dificilmente se encontra um treinamento grátis para mostrar como as coisas funcionam.

Até a próxima!

segunda-feira, 23 de maio de 2016

Diferença entre String, StringBuilder e StringBuffer em Java #3

Dando continuidade na sequência de posts sobre "Diferença entre String, StringBuilder e StringBuffer", hoje vamos comparar a velocidade de processamento entre StringBuilder e StringBuffer. Sugiro dar uma olhada nos post anteriores, caso ainda não tenha visto, clicando aqui.

Diferença entre String, StringBuilder e StringBuffer em Java #3

StringBuilder e StringBuffer são muito rápidos para concatenação de Strings comum e exercem a mesma função, com a principal diferença que, enquanto o StringBuffer é sincronizado, o StringBuilder não!
Imagine várias threads lendo ou alterando a mesma String, e você precisa manter a consistência do do seu código, para esses casos o ideal é utilizar StringBuffer.

No teste a seguir você vai perceber que StringBuilder é mais rápido do que o StringBuffer. Veja o código abaixo:
  • Comparando performance de StringBuilder e StringBuffer
public class ConcatenaString {

    public static void main(String[] args) { 
              
        // Trecho de código para StringBuffer
        StringBuffer strBuffer = new StringBuffer();
        long tStart = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            strBuffer.append("f");
        }

        long tEnd = System.currentTimeMillis();
        long tResult = tEnd - tStart;
        System.out.println("Tempo de execução com StringBuffer = " + tResult + " ms"); 
        
        // Trecho de código para StringBuilder
        StringBuilder strBuilder = new StringBuilder();
        tStart = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            strBuilder.append("f");
        }

        tEnd = System.currentTimeMillis();
        tResult = tEnd - tStart;
        System.out.println("Tempo de execução com StringBuilder = " + tResult + " ms");
    }
}

Tempo de execução com StringBuffer = 5 ms
Tempo de execução com StringBuilder = 3 ms

Muito bem, vimos os problemas em usar String para concatenar utilizando o operador "+", as possíveis soluções para evitar problemas de processamento, vimos a principal diferença entre StringBuilder e StringBuffer, mas e aí? Quando eu devo utilizar o operador "+" para concatenar Strings? Existem situações em que o operador "+" é utilizado, e são aqueles onde queremos manter a qualidade do código em relação a identação, para facilitar a legibilidade do código, sem a necessidade de criar um novo objeto, que também é conhecido como "mult-line Strings".
Veja o código:

public class ConcatenaString {

    public static void main(String[] args) {

        /**
         * Apenas melhorando a legibilidade, sem criar novos objetos
        */
        String strFinal = "Pessoal "
                            + "seja bem vindo "
                            + "ao blog "
                            + "DevIsComing!";

        /**
         * Outro exemplo de uso sem compromoter o desempenho
        */
        int a = 5;
        int b = 15;
        System.out.println("a é igual=" + a + " e b é igual=" + b + " certo!?");
    }
}


Pessoal acabamos por aqui.
Até a próxima!

quinta-feira, 5 de maio de 2016

Debug #6 - Recuperar o ultimo valor com split

O método split é utilizado quando é necessário quebrar uma String em várias substrings.

Veja o seguinte código:
public static void main(String[] args) {
        
        // Como entrada temos um diretorio separado por / barra
        String entrada = "C:/Users/Faguinho/Videos/Seriados/Vikings";
                
        // Vamos quebrar nossa entrada em várias substrings
        // usando / para identificar onde nossa string deverá ser quebrada
        // e armaze-las em um vetor de String
        String[] saida = entrada.split("/");
        
        // Esse laço de repetição está pegando cada nova string gerada
        // a partir da nossa string de entrada, e varrendo todo vetor
        // escrevendo na tela a substring armazenada
        for (String substring : saida) {
            System.out.println(substring);
        }
    }

E teremos a seguinte saida:
C:
Users
Faguinho
Videos
Seriados
Vikings
Acabamos de pegar um determinado diretório e separa-lo em várias pastas até chegar na nossa pasta final que é "Vikings" (uma série muito boa, assistam rs).


Debug 6 - Recuperar o ultimo valor com split


Nos usamos o separador "/" para quebras nossa String, mas poderíamos estar usando qualquer marcador para identificar onde nossa String deveria ser quebrada.
Muitas vezes se faz necessários resgatar o ultimo valor de uma String, e para isso existem inúmeras formas de faze-lo um exemplo clássico é o seguinte código:
public static void main(String[] args) {

        String entrada = "C:/Users/Faguinho/Videos/Seriados/Vikings";
        String[] saida = entrada.split("/");
        
        // Criamos uma variavel do tipo String e a inicializamos com null
        String resultado = null;

        // Nesse laço estamos inicializando nossa variavel em 1
        // iremos ficar repetindo esse código enquanto i for menor que a quantidade
        // de substring armazenadas em nosso vetor saida
        // e estamos informando que nosso laço deve somar 1 a cada execução
        for (int i = 1; i < saida.length; i++) {
            resultado = saida[i];
        }
        
        // E aqui escrevemos o nosso resultado
        System.out.println(resultado);
    }

E teremos a saída: "Vikings"

Mas esse exemplo deixa o código extenso, processando todo um vetor para recuperar o último valor, não é nada bom.

Para resolver esse tipo de problema podemos usar o seguinte código:
public static void main(String[] args) {

        String entrada = "C:/Users/Faguinho/Videos/Seriados/Vikings";
        String resultado = entrada.split( "/" )[entrada.split( "/" ).length - 1];
        
        System.out.println(resultado);
    }

Ficou bonito não? rs
Com apenas uma linha resolvemos todos o nosso problema. Vamos a explicação!

Estamos usando matemática para resolver o nosso problema, então primeiro começaremos pelas [], ou seja nosso segundo split: [entrada.split( "/" ).length - 1], estamos simplesmente verificando a quantidade de substrings geradas menos um.
No nosso exemplo teremos o resultado = 5

A expressão ficou igual a entrada.split( "/" )[5], dessa forma o método split remove todos os valores anteriores e recupera apenas o valor que fica logo após a quinta barra.

Execute esse trecho: entrada.split( "/" )[5], e teremos a nossa saída = Vikings
Execute esse trecho: entrada.split( "/" )[4], e teremos a nossa saída = Seriados
Execute esse trecho: entrada.split( "/" )[2], e teremos a nossa saída = Users

Bom, como eu disse existem várias formas de resolver esse problema, e essa é uma das formas que eu uso nos meus códigos.

Até a próxima.

segunda-feira, 18 de abril de 2016

Capacitação Java - Brasil mais TI

O Ministério da Ciência , Tecnologia e Inovação juntamente com o Ministério da Educação, o Brasil Mais TI que atua em três pontos essenciais da formação profissional:conhecimento, capacitação e oportunidades, está oferecendo totalmente grátis uma capacitação JAVA de 380h divididos em & cursos, todos com certificado.

Capacitação Java - Brasil mais TI


Esse empenho se deve a gargalos na industria de software e serviços de TI crescentes de profissionais qualificados, que poderá atingir aproximadamente a marca de 408 mil em 2020.

Esses são os cursos oferecidos:


Bora lá, faça seu cadastro agora e aproveite a oportunidade!
Abraço

domingo, 17 de abril de 2016

Json com Jackson

Atualmente são inúmeras a quantidade de bibliotecas para se trabalhar com Json, isso se deve a notação leve de troca de dados, a facilidade de interpretação, tanto humana quanto a nível de máquina.
Nesse post eu vou estar explicando como trabalhar com esse carinha aqui, Jackson.

Json com Jackson



Bom, mas antes de falarmos sobre a biblioteca vamos entender um pouco sobre o que é Json. O site oficial descreve Json sendo:

"JSON (JavaScript Object Notation - Notação de Objetos JavaScript) é uma formatação leve de troca de dados.
Para seres humanos, é fácil de ler e escrever. Para máquinas, é fácil de interpretar e gerar.

Está baseado em um subconjunto da linguagem de programação JavaScript.
JSON é em formato texto e completamente independente de linguagem, pois usa convenções que são familiares às linguagens C e familiares, incluindo C++, C#, Java, JavaScript, Perl, Python e muitas outras.

Estas propriedades fazem com que JSON seja um formato ideal de troca de dados.
JSON está constituído em duas estruturas:
  • Uma coleção de pares nome/valor. Em várias linguagens, isto é caracterizado como um object, record, struct, dicionário, hash table, keyed list, ou arrays associativas.
  • Uma lista ordenada de valores. Na maioria das linguagens, isto é caracterizado como uma array, vetor, lista ou sequência.
Estas são estruturas de dados universais. Virtualmente todas as linguagens de programação modernas as suportam, de uma forma ou de outra. É aceitavel que um formato de troca de dados que seja independente de linguagem de programação se baseie nestas estruturas." Para mais informações acesse o site Json-BR.

Já sabemos o motivo da quantidade de bibliotecas existentes, o motivo de tanta informação relacionado a "formatação" JSON que tende a ser usado cada vez mais em várias aplicações.

Agora, sobre a nossa biblioteca, vamos aprender como converter Java para Json e Json para Java.

Vamos considerar o pojo abaixo:

public class Pessoa {

    private String nome;
    private int idade;
    private String profissao;
    private BigDecimal salario;
    private List habilidade;

    //getters and setters
}

Seria necessário configurar e criar a estrutura em um banco de dados, mas para nível de entendimento vamos criar um arquivo texto, dando o nome para o arquivo de "Pessoa.json" e salvar em algum lugar da sua máquina, aqui estarei salvando em "D://Pessoa.json".
Nesse arquivo teremos o seguinte registro:

{"nome":"Fagner Zordan","idade":26,"profissao":"Analista de Sistemas","salario":10000,"habilidade":["java","php","unity3d"]}

Lembre-se Json trabalha com "chave":"valor", um exemplo seria: "nome":"Fagner Zordan".
Note que os atributos da nossa classe "Pessoa" possui os mesmas chaves no nosso arquivo Json.

Já temos nosso pojo criado com os get e set implementados, temos nosso registro no formato Json salvo na nossa máquina, vamos agora converter o objeto Java em JSON:

ObjectMapper mapper = new ObjectMapper();
Pessoa obj = new Pessoa();

//Objeto para JSON salvo no arquivo
mapper.writeValue(new File("D:\\Pessoa.json"), obj);

//Objeto para JSON armazenado em String

String jsonEmString = mapper.writeValueAsString(obj);


E no trecho a seguir convertemos JSON para objeto Java:

ObjectMapper mapper = new ObjectMapper();
String jsonEmString = "{'nome' : 'Fagner Zordan'}";

//JSON do arquivo para Objeto
Pessoa obj = mapper.readValue(new File("D:\\Pessoa.json"), Pessoa.class);

//JSON de uma URL para Objeto
Pessoa obj = mapper.readValue(new URL("http://www.deviscoming.blogspot.com.br/Pessoa.json"), Pessoa.class);

//JSON de uma String para Objeto
Pessoa obj = mapper.readValue(jsonEmString, Pessoa.class);

Viu como é fácil?
Logo estarei disponibilizando um "projeto" (aplicação funcionando usando JSON) no github, e volto a postar aqui.

Por enquanto é isso, abraço e até a próxima.

Primeiros passos com Spring MVC

A empresa Algaworks, localizada em Uberlândia, Minas Gerais, que trabalha desde 2004 com cursos de desenvolvimento de software, está disponibilizando um livreto digital gratuito que ensina primeiros passos sobre Spring MVC.

Primeiros passos com Spring MVC


O que você vai aprender nesse livreto:

1. Introdução

2. O Spring
2.1. Introdução
2.2. Spring vs Java EE
2.3. O Spring Boot
2.4. O Spring MVC
2.5. O Thymeleaf
2.6. O Maven
2.7. O Spring Tool Suite - STS

3. O projeto de gestão de convidados
3.1. Funcionamento
3.2. Criando o projeto no STS
3.3. Criando o controller
3.4. Criando a página
3.5. Rodando o projeto pela primeira vez
3.6. Repositório de convidados
3.7. Enviando um objeto do Controller à View
3.8. Listando objetos com o Thymeleaf
3.9. Adicionando um convidado

4. Conclusão
4.1. Próximos passos

Gostou? Então acesse o site, preencha seu e-mail e abrace essa oportunidade.

Bons estudos e até a próxima!

sexta-feira, 15 de abril de 2016

Kite - Seu copiloto de programação


GO GO! - Congresso de Mobile (Palestras)

Olá!

Anteriormente eu havia postado sobre o evento de "Congresso de Mobile", deixo o link pra quem não acompanhou e gostaria de saber mais sobre.

GO GO! Congresso de Mobile - Videos


Sinto informar mas o evento já acabou, no entanto os organizadores disponibilizaram as 2 palestras mais votadas, e estou deixando o link aqui pra que vocês possam assistir.
Vale lembrar que os videos estarão disponíveis apenas até o dia 18/04/2016.


Nessa palestra Weliton Carlos Flor, fala sobre "Como Criar Aplicativos de Qualidade Para iOS e Android Com Html e JavaScript" usando IONIC, e explica também sobre a diferença entre aplicativos nativos e hibrido.

E nessa outra palestra Robert Souza, fala sobre "Segurança da Informação: Vulnerabilidade em Dispositivos móveis", explica como funciona alguns trojans e vírus,  e explica formas de evita-los.

Pessoal é isso, aproveitem os videos, não foi a toa que esses videos foram selecionados, são palestras muito boas.
Até a próxima.

Debug #4 - Git revert

Com certeza em algum momento da sua vida você já fez um commit na branch master e consequentemente sentiu aquela angustia de "ohhh shitt", e perdeu algum tempo pra conseguir reverter isso sem ter que avisar ninguém sobre a "arte".

xD


Bom estou deixando aqui dois comandos que podem te ajudar.

IMPORTANTE: SÓ REVERTE O ULTIMO COMMIT.

  • Reverte a última alteração do repositório git e mantem suas alterações do commit.
git reset --soft HEAD~1

  • Reverte a última alteração do repositório git e apaga suas alterações.
git reset --hard HEAD~1

Use com moderação rs

Até a próxima!

quinta-feira, 14 de abril de 2016

Integração Advpl x Excel #2

Integração Advpl x Excel #2

Fala galera!

No post anterior nós falamos sobre o início do programa que faz integração Advpl x Excel. Hoje continuaremos com o post a partir da função reportDef().

Vejamos o código antes de prosseguir:

static function reportDef

local oReport
local oSection
local oBreak

//configura o relatório
oReport := TReport():new("RELCLI","Lista de Clientes",,{|oReport| printReport(oReport)},"Lista de Clientes")
oReport:SetLandscape()  //orientação da página como paisagem
oReport:nFontBody := 10 //tamanho da fonte padrão
oReport:showHeader() //define que será exibido o cabeçalho do relatório
//cria uma seção   
oSection := TRSection():new(oReport,"Lista de clientes",{"SA1"})//importante descrever as tabelas utilizadas
oReport:setTotalInLine(.F.)

TRCell():new(oSection, "CLIENTE",,"")
oSection:cell("CLIENTE"):disable()

//define as colunas com o campo da tabela, tabela e cabeçalho que estará na planilha
TRCell():new(oSection, "A1_COD" , "SA1", "Cód. cliente")
TRCell():new(oSection, "A1_NOME", "SA1", "Nome cliente")
TRCell():new(oSection, "A1_END" , "SA1", "Endereço")
TRCell():new(oSection, "A1_MUN" , "SA1", "Cidade")
TRCell():new(oSection, "A1_EST" , "SA1", "Estado")
TRCell():new(oSection, "A1_PAIS", "SA1", "Pais")
      
return oReport

Bom, o que essa função faz basicamente? É responsável por toda a configuração inicial do seu relatório, ou seja, caso o usuário não deseje modificar alguma coisa, como impressão paisagem ou foto, o relatório assumirá a configuração definida nesta função.

Primeiro precisamos instanciar um objeto de TReport e, para isso, definimos o objeto oReport, onde este receberá as configurações iniciais do relatório.

TReport():new("RELCLI","Lista de Clientes",,{|oReport| printReport(oReport)},"Lista de Clientes")

Este método construtor, tem os seguinte parâmetros:

cReport: Nome do relatório. Exemplo: "RELCLI";
cTitle: Título do relatório;
uParam: Parâmetros do relatório cadastrado no Dicionário de Perguntas (SX1). Também pode ser utilizado bloco de código para parâmetros customizados;
bAction: Bloco de código que será executado quando o usuário confirmar a impressão do relatório;
cDescription: Descrição do relatório;
lLandscape: Aponta a orientação de página do relatório como paisagem;
uTotalText: Texto do totalizador do relatório, podendo ser caracter ou bloco de código;
lTotalInLine: Imprime as células em linha;
cPageTText: Texto do totalizador da página;
lPageTInLine: Imprime totalizador da página em linha;
lTPageBreak: Quebra página após a impressão do totalizador;
nColSpace: Espaçamento entre as colunas;


O construtor TRCell() é célula de impressão de uma seção (TRSection) de um relatório que utiliza a classe TReport. 

Para cada célula adicionada ao relatório é necessário chamar este método com a configurações necessárias com o objeto da seção, nome do campo, nome da tabela e título que será apresentado no arquivo excel.

Agora vamos implementar a função que irá efetuar a consulta no banco de dados, salvar em uma tabela temporária e adicionar ao objeto oSection do relatório.
Esta função definimos com o nome de printReport, que executará a impressão do relatório.

static function printReport()
    
local oSection := oReport:section(1)
local cSql := ""

oSection:init()    

//efetua a pesquisa no banco de dados. Neste caso, seleciona todos os clientes existentes
cSql := " SELECT A1_COD, A1_NOME, A1_END, A1_MUN, A1_EST, A1_PAIS "
cSql += " FROM "+RetSqlName("SA1")+"  SA1 "
cSql += " WHERE SA1.D_E_L_E_T_ = '' "   
cSql += " ORDER BY A1_COD "
    
//salva o código sql na pasta TEMP para consultas no seu SGBD
memoWrite("\TEMP\RELSA1.sql",cSql)                           
//Executa a query e cria uma tabela temporária (TRB)
dbUseArea(.T.,"TOPCONN",TCGenQry(,,cSql),"TRB",.F.,.T.)

oReport:setMeter(TRB->(recCount()))

TRB->(dbGoTop())

//percorre a tabela temporária e 'seta' os valores nas respectivas colunas
while !TRB->(eof())
 oSection:cell("A1_COD"):setValue(TRB->A1_COD)
 oSection:cell("A1_NOME"):setValue(TRB->A1_NOME)
 oSection:cell("A1_END"):setValue(TRB->A1_END) 
 oSection:cell("A1_MUN"):setValue(TRB->A1_MUN)
 oSection:cell("A1_EST"):setValue(TRB->A1_EST)
 oSection:cell("A1_PAIS"):setValue(TRB->A1_PAIS)
 oSection:printLine()
 oReport:incMeter()
 TRB->(dbSkip())
endDo
                  
oSection:finish()
TRB->(dbCloseArea())

return


O objeto oSection é iniciado através de seu método init(), onde é responsável por inicializar as configurações e define a primeira página de impressão.
Utilizamos a variável cSql para definirmos a query de consulta ao banco de dados que será posteriormente utilizada.

O método memowrite() é responsável por salvar o código sql que geramos em um diretório, no nosso caso será na pasta Temp do sistema. Este método é muito simples, basta informar a string contendo o diretório onde será salvo o arquivo, com nome e extensão; e a variável que possui o conteúdo a ser salvo.

Já o método dbUseArea() define um arquivo de dados como uma área de trabalho disponível na aplicação. Aqui nós definimos que trabalharemos com uma tabela temporária nomeada TRB, esta tabela terá todos os campos configurados no método reportDef(). Então para acessá-los basta usar a mesma sintaxe que usaríamos com uma tabela real do banco de dados.

Para mostrar ao usuário o andamento da impressão, utilizamos o método oReport:setMeter(TRB->(recCount())) que mostra uma régua de progressão.


//percorre a tabela temporária e 'seta' os valores nas respectivas colunas
while !TRB->(eof())
 oSection:cell("A1_COD"):setValue(TRB->A1_COD)
 oSection:cell("A1_NOME"):setValue(TRB->A1_NOME)
 oSection:cell("A1_END"):setValue(TRB->A1_END) 
 oSection:cell("A1_MUN"):setValue(TRB->A1_MUN)
 oSection:cell("A1_EST"):setValue(TRB->A1_EST)
 oSection:cell("A1_PAIS"):setValue(TRB->A1_PAIS)
 oSection:printLine()
 oReport:incMeter()
 TRB->(dbSkip())
endDo

O laço de repetição while percorre toda a tabela e adiciona o conteúdo dos campos da tabela TRB nas células respectivas. Após o final desse laço de repetição, a seção é encerrada, a tabela temporária é encerrada e a tela de impressão é exibida ao usuário.

Como vocês puderam acompanhar, não é um monstro de sete cabeças realizar essa integração. A linguagem Advpl é muito fácil de trabalhar, pois tem muitos recursos que permitem a alta abstração que nos permite evitar de se preocupar com configurações básicas ou mais complexas.

Então é isso aí! Até mais galera! Qualquer dúvida é só deixar um comentário que responderei assim que puder.

Código fonte completo em GitHub.

terça-feira, 12 de abril de 2016

Diferença entre String, StringBuilder e StringBuffer em Java #2

Esse é o segundo post sobre "Diferença entre String, StringBuilder e StringBuffer em Java", caso tenha perdido o primeiro post clique aqui para acompanhar o assunto.


Anteriormente aviamos avaliado o desempenho quando concatenamos vários caracteres "f", utilizando String. Podemos concluir que utilizar String para concatenar strings pode ser muito prejudicial para o desempenho do seu código.

Hoje vamos analisar a diferença do tempo de processamento entre String e StringBuilder.
Para facilitar, vamos utilizar o mesmo código anterior, realizando pequenas adaptações, para vermos de fato a velocidade de execução do StringBuilder
  • Comparando performance de String e String Builder
public class ConcatenaString {

    public static void main(String[] args) { 
              
        // Trecho de código para String
        String strFinal = "";
        long tStart = System.currentTimeMillis(); 
        
        // Vamos concatenar 100000 vezes o caractere 'f', 
        // então relembremos que cada vez que passarmos no laço 
        // a JVM irá criar um novo objeto em memória.
        for (int i = 0; i < 100000; i++) {
            strFinal += "f";
        }

        long tEnd = System.currentTimeMillis();
        long tResult = tEnd - tStart;
        System.out.println("Tempo de execução com String = " + tResult + " ms"); 
        
        // Trecho de código para StringBuilder
        StringBuilder strBuilder = new StringBuilder();
        tStart = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            strBuilder.append("f");
        }

        tEnd = System.currentTimeMillis();
        tResult = tEnd - tStart;
        System.out.println("Tempo de execução com StringBuilder = " + tResult + " ms");
    }
}
Tempo de execução com String = 14627 ms
Tempo de execução com StringBuilder = 0 ms

Na minha máquina ao executar o código acima, a saída foi de 14627ms para String com o operador "+" e 0ms para o StringBuilder. Foram utilizados apenas 100mil caracteres, mas vamos imaginar se aumentássemos em 10x esse valor... Seria 1milhão de caracteres.

Altere o valor do seu código e verifique a o tempo de processamento.

Perceba que para não tornar o teste tendencioso, coloquei o tInicio e o tFim no início e no fim do laço "for", dessa forma desconsideramos qualquer outro código que venha antes ou depois, por exemplo as atribuições e escritas no console, assim avaliamos puramente a concatenação de ambos.

Sem dúvidas a performance do StringBuilder comparado a String comum é muito melhor quando precisamos concatenar valores.

O que acontece na verdade é que StringBuilder é mútavel, ou seja, a cada "append(valor)" que fizemos no laço, concatenamos de fato um novo valor a String já existente, sem a necessidade da criação de um novo objeto em memória. Mas e qual a diferença entre StringBuilder e StringBuffer? A resposta fica para o próximo post. Até a próxima.