terça-feira, março 09, 2010

Switch com Strings no Java 7

Uma coisa que existe em quase todas as linguagens, exceto Java e C++, é a facilidade de você usar um "switch" com o tipo String. Isso será adicionado ao Java 7.
Peço permissão ao Entanglement para postar um programa que ele mostrou no GUJ:
class TesteSwitch {
    public static void main (String[] args) {
        String s = "desprezível";
        switch (s) {
        case "desprezível":
            System.out.println ("despicable"); break;
        case "auréola":
            System.out.println ("halo"); break;
        case "fungo":
            System.out.println ("fungus"); break;
        case "fujão":
            System.out.println ("fugitive"); break;
        case "abstruso":
            System.out.println ("abstruse"); break;
        }
    }
}
Sendo que esse programa foi transformado pelo compilador para o equivalente a:
class TesteSwitch {
    public static void main (String[] args) {
        String s = "desprezível";
        int hashCode = s.hashCode();
        int i = -1;
        switch (hashCode) {
        case -644512679: if (s.equals ("auréola")) i = 1; 
                         else if (s.equals("desprezível") i = 0; 
                         break;
        case 97793703: if (s.equals ("fujão") i = 3; 
                       else if (s.equals("fungo") i = 2; 
                       break; 
        case 1732918561: if (s.equals ("abstruso") i = 4; 
                         break;
        default: break;
        }
        switch (i) {
        case 0: System.out.println ("despicable"); break;
        case 1: System.out.println ("halo"); break;
        case 2: System.out.println ("fungus"); break;
        case 3: System.out.println ("fugitive"); break;
        case 4: System.out.println ("abstruse"); break;
        }
    }
}

O compilador transforma 1 switch em 2, porque é mais fácil tratar de certos casos (como "fallthrough" e strings que têm exatamente o mesmo hash code). Por exemplo, no caso acima, "auréola" e "desprezível" têm o mesmo hash code, assim como "fujão" e "fungo". E é por isso que o switch checa se o hash code E a string são os mesmos do caso. Se o switch tiver muitos casos, essa implementação é mais rápida que simplesmente ir comparando com "ifs" encadeados.

Obviamente, assim como no caso do "switch" com inteiros, é necessário ver se o seu problema não poderia ser resolvido com hierarquias de classes ou então com enums.

Usando literais binários e separadores de milhar em Java 7

Em Java 7, poderemos usar números binários (embora sejam um pouco extensos), o que facilita a quem precisa codificar máscaras de bits.

Além disso, uma coisa bem mais útil é que podemos agora pôr separadores de dígitos dentro de constantes numéricas.

É bem mais fácil conferir pi = 3.141_592_653_589_793_238_462_643_383_279_5 em vez de 3.1415926535897932384626433832795.

Entretanto, esse suporte a separadores de milhar vale só para o compilador; as rotinas (como Integer.parseInt ou DecimalFormat.parse) não entendem o "_". Isso, provavelmente, é para garantir compatibilidade com programas já existentes.

Uma coisa que sempre me incomodou é que números octais são iniciados por um simples zero, tal como em C/C++. Isso é muito confuso; seria preferível que houvesse um indicador de base (como, digamos, a letra Q. Por exemplo, em vez de 0377, teríamos a constante 0Q377.). Como isso já é uma "pegadinha" antiga da linguagem, eu sugiro que se use sempre um separador, para você tomar cuidado com esse zero inicial. Em vez de usar o número 0377, deveríamos usar 0_377. Mas não sei se uma convenção dessas ajudaria muito ou não.

import java.text.*;

// Requer Java 7 para ser compilada
class NovosLiterais {
    public static void main (String[] args) {
        int cafeBabe = -889275714;
        assert (cafeBabe == 0xCAFEBABE); // hexadecimal
        assert (cafeBabe == 031277535276); // octal
        // 0b introduz um número em notação binária
        assert (cafeBabe == 0b11001010111111101011101010111110); // binário
        // "_" separa os dígitos.
        assert (cafeBabe == 0b1100_1010_1111_1110_1011_1010_1011_1110); // binário
        // Pode-se usar separadores de dígitos em qualquer base
        assert (cafeBabe == 0xCAFE_BABE);
        assert (cafeBabe == -889_275_714);
        assert (cafeBabe == 0_31_277_535_276);
        // Não é preciso pôr os separadores de 3 em 3 ou de 4 em 4. 
        long cnpj = 65_497_745_0001_53L;
        // É possível usar separadores de dígitos mesmo com números de ponto-flutuante
        double pi = 3.141_592_653_589_793_238_462_643_383_279_5;
        double avogadro_constant = 6.022_141_79E+23;
        // Você não pode usar o "_" no início de uma constante, porque aí teríamos
        // um identificador. Ele só pode ser usado dentro de uma constante.
        double _42; // isto é um identificador, não uma constante
        // .parseInt, .parseLong não suportam "_"
 try { 
            long x = Long.parseLong ("CAFEBABE", 16); 
            x = Long.parseLong ("CAFE_BABE", 16);
        } catch (NumberFormatException ex) { ex.printStackTrace(); }
 try { 
            int x = Integer.parseInt ("11001010", 2); 
            x = Integer.parseInt ("1100_1010", 2);
        } catch (NumberFormatException ex) { ex.printStackTrace(); }
        // DecimalFormat.parse também não suporta "_"
        NumberFormat df = DecimalFormat.getInstance();
        try { Number n = df.parse ("123_456"); 
            System.out.println (n); // imprime "123", o que indica que ele não consegue ver além do "_"
        }  catch (ParseException ex) { ex.printStackTrace(); }
    }
}

sexta-feira, março 05, 2010

Usando <> em Java 7

Em Java 7 é possível agora abreviarmos a chamada a um construtor se o tipo tiver parâmetros.
Em vez de termos de repetir todos os tipos parametrizados, é suficiente usarmos <>. Na documentação isso é chamado de "diamond" (losango), em alusão à forma do novo operador.

// Requer Java 7
import java.util.*;

class TesteNotacaoAbreviada {
    public static void listMap (Map<String, String> map) {
        for (Map.Entry<String, String> entry : map.entrySet()) {
        }
    }
    public static void main (String[] args) {
        // Note que não é necessário escrever "new HashMap<String, String>()"
        Map<String, String> traducao = new HashMap<>();        // Nem aqui é preciso escrever "new TreeMap<String, String>()"
        traducao = new TreeMap<>();
        // A notação abreviada funciona também para passagem de parâmetros, mas
        // é bem mais difícil fazê-la funcionar adequadamente. No caso abaixo, 
        // o compilador acha que o tipo que está sendo passado é new TreeMap<Object,Object>
        // e emite um erro de compilação. 
        listMap (new TreeMap<>());
    }
}

Da série "Para eu não me esquecer mais..."

Estou tendo de usar o Compuware DevPartner Studio 9.0.1 (versão de demonstração) para resolver uns problemas de desempenho em um programa em C++, e sempre me esqueço que é necessário arranjar um arquivo chamado wcore-4.9.6658.0.zip, abri-lo, copiar o arquivo wcore.dll para o diretório C:\Program Files\Common Files\Compuware\NMShared\4.9 e reiniciar os serviços do DevPartner.

quinta-feira, março 04, 2010

Identificadores com espaços no Java 7

Se você baixou uma versão recente do JDK 7 (veja http://download.java.net/jdk7 ), pode tentar compilar o seguinte programa.

class #"Teste Identificadores Com Espacos" {
    public static void main (#"String"[] #"argumentos de linha de comando") {
        System.#"out".#"println" ("Os argumentos de linha de comando passados para este programa são:");
        for (#"String" #"argumento" : #"argumentos de linha de comando") {
            System.out.printf ("%s %n", argumento); 
        }
    }
}

Isso foi introduzido pelo John Rose, um pouco "por debaixo dos panos" (já que ele é um dos committers do Sun JDK), e comunicado "after the fact" em: http://wikis.sun.com/display/mlvm/ProjectCoinProposal.
Quem se lembra daqueles identificadores de colunas no SQL (que são cercados por aspas, se contiverem caracteres especiais ou espaços) vai imediatamente reconhecer a utilidade de tais identificadores malucos. O funcionamento é mais ou menos o mesmo: se houver um caracter que não for letra, número, cifrão ou "underscore" em um identificador, você pode "forçar a barra" usando o #"".
Você pode até forçar um pouco mais a barra, e criar uma classe com um nome que é uma palavra reservada do Java.

class #"int" {
}

Só que os seguintes caracteres ainda não são aceitos*, porque são importantes para a JVM distinguir pacotes, nomes de classes, arrays de objetos, e métodos construtores:
. / ; [ ] < >
* Segundo a especificação, bastaria usar um "\\" antes deles, mas o javac não deixa usar esses caracteres mesmo com "\\". Pois é...

terça-feira, janeiro 12, 2010

A classe java.util.BitSet

A classe java.util.BitSet é muito útil para alguns problemas que exigem você saber se um determinado elemento está presente ou não com rapidez e eficiência de memória.
Em vez de usar um byte ou 4 bytes para cada elemento booleano (como você poderia esperar em um boolean[] ou então em um ArrayList), BitSet usa apenas um bit.
Infelizmente, java.util.BitSet não implementa java.util.Set nem java.util.List. Mas ela tem métodos bem interessantes, como cardinality (que conta quantos elementos "true" existem no conjunto, mas de maneira muito eficiente), nextSetBit e nextClearBit (que procuram qual é o próximo elemento "true" ou "false" respectivamente. Em algumas versões da JVM, esses métodos também são implementados com instruções especiais do processador.
Vou dar um exemplo de uso dessa classe, implementando o Crivo de Eratóstenes (para achar os números primos). Para simplificar, não vou efetuar a otimização de indicar no crivo apenas os números ímpares.


import java.util.*;
class Sieve {
private BitSet bs;
public void findPrimes (int n) {
bs = new BitSet (n+1);
int sq = (int) Math.sqrt (n);
// Ao final desta rotina, todos os bits marcados serão primos, e os
// bits desmarcados serão números compostos.
bs.set (0, n - 1, true); // marcando todos os bits como primos...
// Agora vamos achar o primeiro bit primo, e desmarcar todos os bits
// que são múltiplos dele. Vamos começar por 3
int x = 2;
while (x <= sq) {
for (int i = x + x; i <= n; i += x) {
bs.clear (i);
}
// Devemos achar o próximo bit setado que seja maior que x
x = bs.nextSetBit (x + 1);
}
}
public boolean isPrime (int n) {
return bs.get (n);
}
public int nextPrime (int n) {
return bs.nextSetBit (n + 1); // -1 se não houver um próximo
}
public long sumPrimes () {
int n = 1;
long sum = 0;
while ((n = bs.nextSetBit (n + 1)) > 0) {
sum += n;
}
return sum;
}
public static void main(String[] args) {
Sieve s = new Sieve();
s.findPrimes (10);
System.out.println (s.sumPrimes());
s.findPrimes (100);
System.out.println (s.sumPrimes());
s.findPrimes (2000000);
System.out.println (s.sumPrimes());
long n = 600851475147L; // = 3 * 7^2 * 11 * 163 * 2279657
int sqrtN = (int) Math.sqrt (n);

int factor = 2;
int nFactors = 0;
while (n > 1) {
int exponent = 0;
while (n % factor == 0) {
exponent++;
n /= factor;
}
if (exponent != 0) {
System.out.printf ("%d^%d . ", factor, exponent);
nFactors++;
}
factor = s.nextPrime (factor);
if (factor == -1) {
if (n != 1)
System.out.printf ("%d^%d . ", n, 1);
break;
}
}

System.out.println ();
System.out.println (nFactors);
}
}

sexta-feira, junho 06, 2008

Ordenar palavras ignorando acentos

Volta e meia você quer ordenar palavras ignorando a caixa (minúscula/maiúscula) e os acentos e cedilhas; a reação normal é criar um método que passe tudo para minúsculas e remova acentos e cedilhas. Embora isso seja necessário às vezes, é melhor usar um recurso do Java chamado "Collators". Exemplo:

import java.util.*;
import java.text.*;

class ExemploCollator {
public static void main(String[] args) {
// Vamos comparar duas strings ignorando os acentos.
String st1 = "Gisele da Conceição Zózima Bündchen";
String st2 = "Gisele da Conceicao Zozima Bundchen";
String st3 = "GISELE DA CONCEICAO ZÓZIMA BUNDCHEN";
String st4 = "Gisele da Conceicao Zozimo Bundchen"; // note que esta string é diferente
Collator collator = Collator.getInstance (new Locale ("pt", "BR"));
collator.setStrength(Collator.PRIMARY); // importante!
if (collator.compare (st1, st2) == 0) {
System.out.println ("As duas Giseles são a mesma pessoa, só diferem pelos acentos");
}
if (collator.compare (st1, st3) == 0) {
System.out.println ("As duas Giseles são a mesma pessoa, só diferem pelos acentos e pela diferença de

caixa");
}
if (collator.compare (st1, st4) != 0) {
System.out.println ("As duas Giseles não são a mesma pessoa");
}

// Agora vamos mostrar um exemplo de ordenação ignorando os acentos.
String[] dados = {
"José Aparecido",
"João Simões",
"Jó Abedenego"
};
// Não usando "collator"
SortedSet<String> s1 = new TreeSet<String>();
for (String d : dados) {
s1.add (d);
}
// Deve imprimir [José Aparecido, João Simões, Jó Abedenego], que é
// ao contrário das regras da língua portuguesa
System.out.println (s1);
// Usando "collator", segue as regras:
// imprime "[Jó Abedenego, João Simões, José Aparecido]"
final Collator coll = Collator.getInstance (new Locale ("pt", "BR"));
SortedSet<String> s2 = new TreeSet<String>(new Comparator<String>() {
public int compare (String o1, String o2) {
return coll.compare (o1, o2);
}
});
for (String d : dados) {
s2.add (d);
}
System.out.println (s2);
// O código abaixo é equivalente e mais simples,
// porque Collator implementa Comparator.
s2 = new TreeSet<String>(coll);
System.out.println (s2);
}
}

sexta-feira, maio 30, 2008

Criptografia RSA

Posto abaixo um programa que escrevi faz alguns anos para servir de exemplo de uso das APIs de criptografia do Java. Dei uma reformatada (com o bom e velho Eclipse) e incluí alguns javadocs. Não é o jeito "correto" de usar as APIs, mas muita gente precisa de algo parecido com isso para trabalhos de escola, portanto divirtam-se!

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.CertificateException;
import java.security.spec.RSAKeyGenParameterSpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

// Exemplo de criptografia RSA.
//
// Para criptografar um bloco de dados com RSA, gera-se uma chave aleatória para
// usar com um algoritmo simétrico (como o AES), e então criptografa-se essa chave
// com o algoritmo RSA usando-se a chave pública do destinatário.
//
// Para o destinatário decifrar os dados, ele deve decifrar a chave recebida com sua
// chave privada, e então usar a chave decifrada para decifrar os dados.
//
// Note que o modo correto de fazer isso é usar um formato padronizado, como o PKCS#7
// (ou CMS), e usar o BouncyCastle. Só faço isto para exemplificar as APIs do Java.
//
/**
* Efetua a parte da criptografia
*/
class Cifrador {
/**
* Cifra um bloco de dados com a chave pública.
*
* @param pub
* A chave pública.
* @param textoClaro
* O bloco de dados a ser cifrado.
* @return Dois arrays de bytes, sendo que o primeiro é o bloco cifrado, e o
* segundo é a chave gerada e cifrada. Não jogue fora nenhum deles.
* @throws NoSuchAlgorithmException
* Algoritmo (AES) não disponível na sua versão do JDK.
* @throws NoSuchPaddingException
* Padding (PKCS5Padding) não disponível na sua versão do JDK.
* @throws InvalidKeyException
* Se a chave pública for inválida.
* @throws IllegalBlockSizeException
* Não deve ocorrer.
* @throws BadPaddingException
* Não deve ocorrer.
* @throws InvalidAlgorithmParameterException
* Não deve ocorrer.
*/
public byte[][] cifra(PublicKey pub, byte[] textoClaro)
throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, IllegalBlockSizeException,
BadPaddingException, InvalidAlgorithmParameterException {
byte[] textoCifrado = null;
byte[] chaveCifrada = null;

// -- A) Gerando uma chave simétrica de 128 bits
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(128);
SecretKey sk = kg.generateKey();
byte[] chave = sk.getEncoded();
// -- B) Cifrando o texto com a chave simétrica gerada
Cipher aescf = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivspec = new IvParameterSpec(new byte[16]);
aescf.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(chave, "AES"),
ivspec);
textoCifrado = aescf.doFinal(textoClaro);
// -- C) Cifrando a chave com a chave pública
Cipher rsacf = Cipher.getInstance("RSA");
rsacf.init(Cipher.ENCRYPT_MODE, pub);
chaveCifrada = rsacf.doFinal(chave);

return new byte[][] { textoCifrado, chaveCifrada };
}
}

/**
* Efetua a parte da decifração
*/
class Decifrador {
/**
* Decifra o bloco de dados e a chave cifrada usando a chave privada do destinatário.
* @param pvk A chave privada.
* @param textoCifrado Os dados cifrados.
* @param chaveCifrada A chave cifrada.
* @return Os dados decifrados.
* @throws NoSuchAlgorithmException Algoritmo AES não disponível na sua versão do JDK.
* @throws NoSuchPaddingException PKCS5Padding não disponível na sua versão do JDK.
* @throws InvalidKeyException Se a chave passada for inválida.
* @throws IllegalBlockSizeException Não deve ocorrer.
* @throws BadPaddingException Se houver um erro de decifração (chave incorreta ou texto
* cifrado incorreto, por exemplo)
* @throws InvalidAlgorithmParameterException Não deve ocorrer.
*/
public byte[] decifra(PrivateKey pvk, byte[] textoCifrado,
byte[] chaveCifrada) throws NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException,
IllegalBlockSizeException, BadPaddingException,
InvalidAlgorithmParameterException {
byte[] textoDecifrado = null;

// -- A) Decifrando a chave simétrica com a chave privada
Cipher rsacf = Cipher.getInstance("RSA");
rsacf.init(Cipher.DECRYPT_MODE, pvk);
byte[] chaveDecifrada = rsacf.doFinal(chaveCifrada);
// -- B) Decifrando o texto com a chave simétrica decifrada
Cipher aescf = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivspec = new IvParameterSpec(new byte[16]);
aescf.init(Cipher.DECRYPT_MODE,
new SecretKeySpec(chaveDecifrada, "AES"), ivspec);
textoDecifrado = aescf.doFinal(textoCifrado);

return textoDecifrado;
}
}
/**
* Serve para carregar a chave pública de um arquivo. Não é o melhor jeito
* de guardar a chave (é melhor e mais seguro usar um keystore),
* mas faço isso só para simplificar.
*/
class CarregadorChavePublica {
/**
* Carrega a chave pública serializada de um arquivo.
* @param fPub O arquivo com a chave pública serializada.
* @return A chave pública
* @throws IOException Se não achar o arquivo, ou se houver algum problema
* ao efetuar a des-serialização.
* @throws ClassNotFoundException O objeto contido no arquivo é de uma classe não presente
* neste projeto.
*/
public PublicKey carregaChavePublica(File fPub) throws IOException,
ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fPub));
PublicKey ret = (PublicKey) ois.readObject();
ois.close();
return ret;
}
}

/**
* Serve para carregar a chave privada de um arquivo. Não é o melhor jeito
* de guardar a chave (é melhor e mais seguro usar um keystore),
* mas faço isso só para simplificar.
*/
class CarregadorChavePrivada {
/**
* Carrega a chave privada serializada de um arquivo.
* @param fPvk O arquivo com a chave privada serializada.
* @return A chave privada.
* @throws IOException Se não achar o arquivo, ou se houver algum problema
* @throws ClassNotFoundException O objeto contido no arquivo é de uma classe não presente
* neste projeto.
*/
public PrivateKey carregaChavePrivada(File fPvk) throws IOException,
ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fPvk));
PrivateKey ret = (PrivateKey) ois.readObject();
ois.close();
return ret;
}
}

/**
* Gera um par de chaves e as guarda em formato serializado.
* (não é o formato mais seguro - seria melhor usar um keystore, que pode ser protegido
* por senha), mas faço isto para simplificar a compreensão.
*/
class GeradorParChaves {
private static final int RSAKEYSIZE = 1024;

/**
* Gera um par de chaves e as guarda em formato serializado em arquivos.
* @param fPub O arquivo que irá conter a chave pública.
* @param fPvk O arquivo que irá conter a chave privada.
* @throws IOException Problemas de acesso/gravação do arquivo.
* @throws NoSuchAlgorithmException RSA não disponível nesta versão do JDK.
* @throws InvalidAlgorithmParameterException Não deve ocorrer.
* @throws CertificateException Não deve ocorrer.
* @throws KeyStoreException Não deve ocorrer.
*/
public void geraParChaves(File fPub, File fPvk) throws IOException,
NoSuchAlgorithmException, InvalidAlgorithmParameterException,
CertificateException, KeyStoreException {

KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(new RSAKeyGenParameterSpec(RSAKEYSIZE,
RSAKeyGenParameterSpec.F4));
KeyPair kpr = kpg.generateKeyPair();
PrivateKey priv = kpr.getPrivate();
PublicKey pub = kpr.getPublic();

// -- Gravando a chave pública em formato serializado
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(
fPub));
oos.writeObject(pub);
oos.close();

// -- Gravando a chave privada em formato serializado
// -- Não é a melhor forma (deveria ser guardada em um keystore, e
// protegida por senha),
// -- mas isto é só um exemplo
oos = new ObjectOutputStream(new FileOutputStream(fPvk));
oos.writeObject(priv);
oos.close();

}
}

/**
* A classe a seguir cifra a decifra a string "Hello, world!".
*/
class ExemploCriptografia {
/**
* Mostra um array de bytes como um "dump".
* @param b O array de bytes que deve ser mostrado em formato dump.
*/
static void printHex(byte[] b) {
if (b == null) {
System.out.println("(null)");
} else {
for (int i = 0; i < b.length; ++i) {
if (i % 16 == 0) {
System.out.print(Integer
.toHexString((i & 0xFFFF) | 0x10000)
.substring(1, 5)
+ " - ");
}
System.out.print(Integer.toHexString((b[i] & 0xFF) | 0x100)
.substring(1, 3)
+ " ");
if (i % 16 == 15 || i == b.length - 1) {
int j;
for (j = 16 - i % 16; j > 1; --j)
System.out.print(" ");
System.out.print(" - ");
int start = (i / 16) * 16;
int end = (b.length < i + 1) ? b.length : (i + 1);
for (j = start; j < end; ++j)
if (b[j] >= 32 && b[j] <= 126)
System.out.print((char) b[j]);
else
System.out.print(".");
System.out.println();
}
}
System.out.println();
}
}

/**
* O programa principal
* @param args Os argumentos são ignorados.
* @throws Exception Lanço uma Exception para não poluir muito o código de demonstração
* com tratamento de exceções. Em um programa "de verdade" você deveria
* tratar corretamente as exceções.
*/
public static void main(String[] args) throws Exception {
// -- Gera o par de chaves, em dois arquivos (chave.publica e
// chave.privada)
GeradorParChaves gpc = new GeradorParChaves();
gpc.geraParChaves(new File("chave.publica"), new File("chave.privada"));

// -- Cifrando a mensagem "Hello, world!"
byte[] textoClaro = "Hello, world!".getBytes("ISO-8859-1");
CarregadorChavePublica ccp = new CarregadorChavePublica();
PublicKey pub = ccp.carregaChavePublica(new File("chave.publica"));
Cifrador cf = new Cifrador();
byte[][] cifrado = cf.cifra(pub, textoClaro);
printHex(cifrado[0]);
printHex(cifrado[1]);

// -- Decifrando a mensagem
CarregadorChavePrivada ccpv = new CarregadorChavePrivada();
PrivateKey pvk = ccpv.carregaChavePrivada(new File("chave.privada"));
Decifrador dcf = new Decifrador();
byte[] decifrado = dcf.decifra(pvk, cifrado[0], cifrado[1]);
// System.out.println (new String (textoClaro, "ISO-8859-1"));
printHex(decifrado);
}
}

terça-feira, maio 27, 2008

Formatação de Números

Se você deseja converter um double ou um BigDecimal de/para uma string em formato monetário - R$ 12.345.678,90 por exemplo - então use java.util.NumberFormat , método getCurrencyInstance. Isso economiza um monte de tempo tentando tirar e pôr pontos, vírgulas e outras coisas ineficientes (e que podem estar erradas também).
Exemplo:
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;

class NumberFormatComMoeda {
public static void main(String[] args) {
NumberFormat nf = NumberFormat.getCurrencyInstance (new Locale ("pt", "BR"));
double d = 12345678.90;
String s = nf.format (d);
System.out.println (s); // deve imprimir "R$ 12.345.678,90"
// Para os que preferem double:
try {
double d1 = nf.parse (s).doubleValue();
System.out.println (d1); // imprime "1.23456789E7"
} catch (ParseException ex) {
ex.printStackTrace();
}
// Para os que preferem BigDecimal:
((DecimalFormat) nf).setParseBigDecimal(true);
try {
BigDecimal bd = (BigDecimal) (nf.parse(s));
System.out.println (bd); // imprime "12345678.90"
} catch (ParseException ex) {
ex.printStackTrace();
}
}
}

quarta-feira, abril 23, 2008

Cliff Click's NonBlocking HashTable

Cliff Click has made available his new brainchild, the NonBlocking HashTable.

He claims that it is faster than java.util.concurrent.ConcurrentHashMap.

Check: http://blogs.azulsystems.com/cliff/2007/04/nonblocking_has.html

http://sourceforge.net/projects/high-scale-lib

DecimalFormat, SimpleDateFormat - Threading Issues

The classes java.text.DecimalFormat and java.text.SimpleDateFormat are very useful, but Sun's implementation is not thread-safe. It means that if you want to have a static instance of any of these classes in your EJB, Servlet or JSP page, you can have sporadic problems with incorrect formatting.
You'll need to use a ThreadLocal, or use a local object.
ThreadLocal sample usage:

private static ThreadLocal defaultCurrencyFormat  = new ThreadLocal() {
protected synchronized Format initialValue() {
return new DecimalFormat ("0.00",
new DecimalFormatSymbols (new Locale ("pt", "BR")));
}
};

...

String s = defaultCurrencyFormat.get().format (123.45);

You'll need to use an additional "get".

quarta-feira, março 22, 2006

Recommended reading for Bouncy Castle users

If you're interested in using the Bouncy Castle JCE Library, you'll need to check this book:
Beginning Cryptography with Java, by David Hook.

You can download the examples of the book, if you just want to have a test suite for Bouncy Castle.