Dual Boot: Windows 7 e Ubuntu 12.04 em um Particionamento GPT

Recentemente adquiri um novo notebook cujo disco veio particionado com a arquitetura GPT (GUID Partition Table). Como é de costume, optei por deixar tanto o Windows quanto o Linux instalados (Dual Boot). Esta tarefa costumava ser simples nas máquinas que tive antes, particionadas com MBR, mas tive certa dificuldade para conseguir criar o Dual boot com o GPT.

Apesar de todo o particionamento manual estar aparentemente certo, depois de instalar o Windows, e na sequência o Ubuntu, como geralmente faço, o GRUB não era apresentado no momento do boot para a escolha do SO a ser utilizado, sendo o Ubuntu inicializado automaticamente.

Depois de procurar um pouco encontrei um post que me ajudou a fazer a instalação com Dual Boot com sucesso. Este post me levou a outro, em cujos comentários estão os passos fundamentais para o sucesso.

Antes de comentar estes passos, vale lembrar que eu já havia criado as partições e já tinha realizado a instalação do Windows 7 e do Ubuntu, em suas devidas partições. O único ponto que ainda faltava era fazer o Ubuntu reconhecer a partição do Windows e apresentar as opções no GRUB. Inclusive ao realizar a inicialização pelo Ubuntu, era possível acessar as partições do Windows.

Eis o ponto fundamental a partir daqui (conforme comentário no post citado acima):

The GRUB menu can be unhidden by commenting out the two lines regarding GRUB_HIDDEN in /etc/defaults/grub and running update-grub as root.

Next, the entry for windows can be manually added by appending the following lines to /etc/grub.d/40_custom:
menuentry “Windows” {
    search –fs-uuid –no-floppy –set=root YOUR-EFI-PARTITIONS-UUID-HERE
    chainloader (${root})/efi/Microsoft/Boot/bootmgfw.efi
}

Find your EFI partitions UUID by running ‘ls -la /dev/disk/by-uuid/’. As the EFI partition is a FAT32 partition, the UUID is of the form XXXX-XXXX. If you have more than one FAT partition, you can verify if one is the EFI partition by checking the partition map with gdisk (not installed by default). Run gdisk on the device, ‘sudo gdisk /dev/DEVICE’, press ‘p’ to print the partition table, and then ‘q’ to quit. DON’T make any changes to the partition table. The EFI partition will have the code type ‘EF00’ and most likely a name/label that says it is a EFI system partition.

Então, conforme citado no comentário, é necessário comentar o trecho de configuração que oculta o GRUB. Isto pode ser feito comentando as linhas que iniciam com GRUB_HIDDEN no arquivo /etc/defaults/grub:

dual_boot1

Depois disso é necessário incluir uma entrada no arquivo /etc/grub.d/40_custom para a opção de menu do GRUB para o Windows:

dual_boot2

Na imagem acima, o valor BA69-59A5 é o UUID da partição EFI, criada durante o particionamento (ver comentários no primeiro post referenciado). Para se chegar a este valor é necessário executar o comando ls -la /dev/disk/by-uuid/, que dará o UUID da partição EFI no formato XXXX-XXXX (por ser uma partição FAT32):

dual_boot3

Depois de tudo feito execute o comando sudo update-grub e reinicie o sistema. Se tudo der certo o GRUB aparecerá com as opções de boot.

É isso galera, até a próxima!

Anúncios

Configurando o JBoss Tools 3.3.1 no Eclipse Indigo

Bem, depois de tanto tempo volto a me dedicar um pouco ao blog. Já faz realmente muito tempo desde que escrevi o último artigo.

Pretendo voltar à ativa por aqui com vários assuntos, muitos dos quais já de conhecimento de muitos desenvolvedores, mas procurando dar uma visão diferente e mais prática, como sempre tenho procurado fazer.

Bom, este post trata de um assunto que já foi tema de outros posts. No post Preparando um Ambiente de Desenvolvimento Java EE Baseado em Eclipse eu demonstrei como preparar um ambiente de desenvolvimento baseado no Eclipse utilizando os principais plugins. Já no post Adicionando Servidor de Aplicações JBoss ao Eclipse eu mostrei como adicionar o servidor de aplicações JBoss ao Eclipse, para facilitar o desenvolvimento das aplicações e controle do servidor através da IDE.

No entanto, mesmo que esta seja uma tarefa trivial para quem já desenvolve a um certo tempo, e para muitos nem mereça um post, para muitas pessoas esta não é uma tarefa tão simples e intuitiva. Prova disso são as experiências que tenho como professor em um curso de especialização em Java, onde muitas vezes os alunos não sabem sequer como configurar o workspace adequadamente com uma JDK.

Pensando nisto, e principalmente em meus atuais e futuros alunos, é que me disponho a postar coisas simples como esta, que são de grande ajuda para quem está começando.

Neste post não vou mostrar toda a configuração de um ambiente do ZERO novamente; para isto você pode consultar o post citado anteriormente, fazendo as devidas ressalvas com relação à versão da IDE e dos plugins.

O que vou mostrar aqui é algo mais direto: como instalar uma versão adequada do JBoss Tools ao Eclipse Indigo. Para isto vou assumir que você já tenha o Eclipse Indigo instalado, e se não tem você pode baixá-lo a partir do site http://www.eclipse.org/downloads/. Eu recomendo que você baixe a versão Eclipse IDE for Java EE Developers, que já traz alguns plugins voltados ao desenvolvimento JEE. Escolha a versão adequada para seu sistema operacional e, após efetuar o download, descompacte o arquivo em seu local de escolha.

Uma vez instalado, inicie o Eclipse:

Uma vez inicializado o Eclipse, é importante escolher a perspectiva Java EE para se trabalhar com o desenvolvimento JEE, e principalmente para facilitar a configuração de um servidor de aplicações. Se você selecionou, na inicialização, um workspace novo, o IDE terá a seguinte aparência (provavelmente):

Repare que, no canto superior direito a perspectiva Java EE está selecionada, e na parte inferior a aba de servidores está selecionada.

Se quisermos, por exemplo, adicionar o servidor de aplicações JBoss 6.1 em novo ambiente, podemos clicar no link “new server wizard”. Ao fazer isso um wizard será apresentado, e nele podemos observar que a opção de adicionar o JBoss 6.1 não está disponível:

Se clicarmos no link “Download additional server adapters”, a seguinte tela será apresentada, nos dando a opção de baixar mais plugins para outros servidores, dentre ele o JBoss Tools:

Ao selecionar a opção desejada e avançar, os procedimentos seguintes levarão à instalação do plugin. Os passos seguintes são bem intuitivo.

Outra maneira de instalar o plugin do JBoss Tools é através de um arquivo local, mas eu sinceramente não gosto desta opção. As experiências que tive mostraram que esta é uma opção mais demorada e propensa a erros, mas ainda assim pode ser uma opção para quem está sem conexão no momento de fazer a configuração. Para isso será necessário baixar o arquivo do site do projeto JBoss Tools, mas por não considerar esta opção a melhor, vou ignorá-la e apresentar a forma que, assim como a primeira forma apresentada, é para mim mais adequada.

Acesse o site http://www.jboss.org/tools/download. No link de downloads é possível encontrar a versão estável do plugin para o Eclipse Indigo. Esta é a principal razão pela qual não utilizo ainda o Eclipse Juno, que é a última versão do IDE Eclipse; o plugin do JBoss Tools ainda não é estável para ele.

Bem, após clicar no link voc~e será redirecionado à uma página onde é possível ter acesso ao update site do plugin, que é um link que usaremos para a configuração do mesmo no Eclipse:

Copie o link apresentado e, voltando ao Eclipse, selecione o menu “Install New Software…”:

Na tela seguinte clique no botão “Add…” e a seguinte janela de configuração será apresentada, onde é possível inserir o link copiado da página do plugin e indicar um nome para o mesmo:

Depois disso os recursos do plugin serão apresentados para que você escolha qual deles quer instalar. Eu aconselho a instalação de todos. Sendo assim selecione todas as opções apresentadas e clique no botão “Next >”:

Dependendo da banda de sua conexão, os passos seguintes podem ser bem demorados. Basicamente você terá que confirmar as escolhas feitas, aceitar alguns termos de licença e aguardar o download dos itens. Ao final será solicitado que a IDE seja reinicializada. Simplesmente aceite.

Depois de reiniciada a IDE, tente novamente adicionar o servidor de aplicações JBoss 6.1 à IDE. Você verá que o adaptador estará disponível.

Para os passos seguintes da configuração do servidor você pode consultar o post referenciado no início deste post, pois o processo será basicamente o mesmo.

É importante ressaltar que não basta ter uma JRE configurada no Eclipse. É preciso ter uma JDK configurada. Os procedimentos para esta configuração também podem ser encontrados em um dos posts citados no início deste post, referente à criação de um ambiente de desenvolvimento completo baseado no eclipse.

Bem, é isto. Espero que esta dica seja proveitosa para muitos que estão começando.

Até o próximo post!

Os números de 2011

Os duendes de estatísticas do WordPress.com prepararam um relatório para o ano de 2011 deste blog.

Aqui está um resumo:

A sala de concertos da Ópera de Sydney tem uma capacidade de 2.700 pessoas. Este blog foi visitado cerca de 36.000 vezes em 2011. Se fosse a sala de concertos, eram precisos 13 concertos egostados para sentar essas pessoas todas.

Clique aqui para ver o relatório completo

WeaverFX – Um concorrente para o Apache Pivot?

Esta semana, participando de um post do GUJ, fiquei sabendo da existência de uma plataforma RIA que muito se assemelha ao Apache Pivot.

O WeaverFX tem a mesma proposta: uma plataforma que permita a criação de aplicações ricas que possam rodar tanto no desktop quanto na web, na forma de applets.

Só hoje tirei um tempo para olhar o site, e ver alguns exemplos. Minha opinião pessoal? Não gostei.

Tenho que admitir que não me dei ao trabalho de criar uma aplicação de exemplo, me contentando apenas com os exemplos apresentados no site do projeto, mas isso foi devido à primeira impressão que tive.

Primeiramente há o fato de que é necessário instalar uma runtime específica para que se possa executar as aplicações. Uma vez que o Apache Pivot não me exige isto, já conto um ponto negativo para o WeaverFX em relação ao Apache Pivot.

 

 

Outra coisa que definitivamente não me agradou foi o “acabamento” dos componentes, e do resultado final das aplicações.

 

 

 

 

 

Apenas para efeito comparativo, eis uma imagem de uma aplicação que fiz em Pivot:

 

 

Ok, o post é tendencioso, admito que prefiro o Apache Pivot, mas também não me coloco na obrigação de ser imparcial =)

Há pessoas que gostaram do WeaverFX, e acredito que realmente é uma questão de escolha. Eu já fiz a minha =)

 

Para quem quiser saber mais sobre o projeto, acessem o site: http://www.weaverfx.com/

Jogo da Memória com Apache Pivot

É grande com satisfação que posto este pequeno tutorial!

Tenho percebido, pelas estatísticas do blog, que o “Jogo da Memória em Java”, um de meus primeiros posts neste blog, é o tutorial mais acessado.

A grande maioria da galera que acessa este tutorial está buscando uma solução fácil para algum trabalho de faculdade, mas no fundo, este tutorial representou muito mais pra mim. Foi meu primeiro desafio pessoal em Java, algo que eu quis fazer para realmente me desafiar, e aprender a programar em Swing.

Agora, tenho o prazer de postar aqui o mesmo jogo, com algumas pequenas modificações  funcionais, escrito na plataforma Pivot.

Sim! Apesar de ser escrito em Java, o Apache Pivot é uma plataforma RIA completa.

Não vou entrar em detalhes para explicar o código. Ao invés disso, vou disponibilizar aqui o código do jogo, e deixar o desafio de executar o jogo para aqueles que se interessarem em aprender mais sobre esta fascinante plataforma RIA.

Este jogo, apesar de pequeno, demonstra muitos dos recursos disponíveis no Pivot.

Visitem o site do projeto, e leiam os tutoriais http://pivot.apache.org/.

A versão 2.0 estará saindo em breve (tenho acompanhado a lista de desenvolvimento e de usuários, e recomendo).

 

 

 

Definição da interface através do arquivo memgame.wtkx:

 


<Window title="Pivot's Memory Game" maximized="true"
    xmlns:wtkx="http://pivot.apache.org/wtkx"
    xmlns:content="org.apache.pivot.wtk.content"
    xmlns="org.apache.pivot.wtk">
    <content>
        <Border>
            <content>
	            <GridPane columnCount="6" styles="{horizontalSpacing:1, verticalSpacing:1,
                    showHorizontalGridLines:true, showVerticalGridLines:true}">
                    <rows>
                        <GridPane.Row>
                            <PushButton toggleButton="true" wtkx:id="1">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
                            <PushButton toggleButton="true" wtkx:id="2">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
                            <PushButton toggleButton="true" wtkx:id="3">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
                            <PushButton toggleButton="true" wtkx:id="4">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
				            <PushButton toggleButton="true" wtkx:id="5">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
				            <PushButton toggleButton="true" wtkx:id="6">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
                        </GridPane.Row>
                        <GridPane.Row>
                            <PushButton toggleButton="true" wtkx:id="7">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
                            <PushButton toggleButton="true" wtkx:id="8">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
                            <PushButton toggleButton="true" wtkx:id="9">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
                            <PushButton toggleButton="true" wtkx:id="10">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
				            <PushButton toggleButton="true" wtkx:id="11">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
				            <PushButton toggleButton="true" wtkx:id="12">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
                        </GridPane.Row>
                        <GridPane.Row>
                            <PushButton toggleButton="true" wtkx:id="13">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
                            <PushButton toggleButton="true" wtkx:id="14">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
                            <PushButton toggleButton="true" wtkx:id="15">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
                            <PushButton toggleButton="true" wtkx:id="16">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
				            <PushButton toggleButton="true" wtkx:id="17">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
				            <PushButton toggleButton="true" wtkx:id="18">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
                        </GridPane.Row>
                        <GridPane.Row>
                            <PushButton toggleButton="true" wtkx:id="19">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
                            <PushButton toggleButton="true" wtkx:id="20">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
                            <PushButton toggleButton="true" wtkx:id="21">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
                            <PushButton toggleButton="true" wtkx:id="22">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
				            <PushButton toggleButton="true" wtkx:id="23">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
				            <PushButton toggleButton="true" wtkx:id="24">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
                        </GridPane.Row>
                        <GridPane.Row>
                            <PushButton toggleButton="true" wtkx:id="25">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
                            <PushButton toggleButton="true" wtkx:id="26">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
                            <PushButton toggleButton="true" wtkx:id="27">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
                            <PushButton toggleButton="true" wtkx:id="28">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
				            <PushButton toggleButton="true" wtkx:id="29">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
				            <PushButton toggleButton="true" wtkx:id="30">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
                        </GridPane.Row>
                        <GridPane.Row>
                            <PushButton toggleButton="true" wtkx:id="31">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
                            <PushButton toggleButton="true" wtkx:id="32">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
                            <PushButton toggleButton="true" wtkx:id="33">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
                            <PushButton toggleButton="true" wtkx:id="34">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
				            <PushButton toggleButton="true" wtkx:id="35">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
				            <PushButton toggleButton="true" wtkx:id="36">
				                <buttonData>
				                    <content:ButtonData text=""/>
				                </buttonData>
				            </PushButton>
                        </GridPane.Row>
                    </rows>
                </GridPane>
            </content>
        </Border>
    </content>
</Window>

Classe que manipula os componentes de tela:


package votti.pivot.memgame;

import org.apache.pivot.collections.Map;
import org.apache.pivot.wtk.Alert;
import org.apache.pivot.wtk.Application;
import org.apache.pivot.wtk.Button;
import org.apache.pivot.wtk.ButtonPressListener;
import org.apache.pivot.wtk.DesktopApplicationContext;
import org.apache.pivot.wtk.Display;
import org.apache.pivot.wtk.MessageType;
import org.apache.pivot.wtk.PushButton;
import org.apache.pivot.wtk.Window;
import org.apache.pivot.wtkx.WTKXSerializer;

import votti.pivot.memgame.component.MemGameButtonData;

public class Main implements Application, ButtonPressListener {

	private String defaultImage = "img/default.gif";
	private boolean firstClick = true;
	private boolean right = true;
	private PushButton buttonOne;
	private PushButton buttonTwo;
	private Button clickedButtonOne;
	private Button clickedButtonTwo;

    private Window window = null;
    private String[] images18;
    private String[] images36;
    private PushButton[] buttons;

    @Override
    public void startup(Display display, Map<String, String> properties)

        throws Exception {
        WTKXSerializer wtkxSerializer = new WTKXSerializer();
        window = (Window) wtkxSerializer.readObject(this, "memgame.wtkx");

        prepareImagesArray();

        buttons = new PushButton[36];

        for(int aux = 0; aux < 36; aux++){
        	buttons[aux] = (PushButton) wtkxSerializer.get(String.valueOf(aux+1));
        	buttons[aux].setButtonData(new MemGameButtonData(defaultImage, images36[aux]));
        	buttons[aux].getButtonPressListeners().add(this);
        }

        window.open(display);
        window.setWidth(500);
        window.setHeight(520);
    }

    @Override
    public boolean shutdown(boolean optional) {
        if (window != null) {
            window.close();
        }

        return false;
    }

    @Override
    public void suspend() {
    }

    @Override
    public void resume() {
    }

    public static void main(String[] args) {
        DesktopApplicationContext.main(Main.class, args);
    }

    private void prepareImagesArray(){

		int posicaoNoArray, x, y;
		this.images18 = new String[18];

		for ( x = 0; x < (18); x++ ){
			this.images18[ x ] = new String("img/" + ( x + 1 ) + ".gif");
		}

		this.images36 = new String[ (36) ];

		for ( x = 0; x < 2; x++ ){
			for ( y = 0; y < (18); y++ ){
				do{
					posicaoNoArray = ( int ) ( Math.random() * (36) );
				}while( this.images36[ posicaoNoArray ] != null );
				this.images36[ posicaoNoArray ] = images18[ y ];
			}
		}
	}

	@Override
	public void buttonPressed(Button button) {

		if( firstClick ){

			if ( !right ){

				buttonOne = (PushButton) clickedButtonOne;
				buttonTwo = (PushButton) clickedButtonTwo;

				((MemGameButtonData)buttonOne.getButtonData()).setDefaultURL();
				((MemGameButtonData)buttonTwo.getButtonData()).setDefaultURL();

				window.repaint();
			}

			clickedButtonOne = button;

			buttonOne = (PushButton) clickedButtonOne;
			((MemGameButtonData)buttonOne.getButtonData()).setButtonURL();

			firstClick = !firstClick;
		}
		else{

			clickedButtonTwo = button;
			buttonTwo = (PushButton) clickedButtonTwo;

			if ( clickedButtonTwo == clickedButtonOne ){

				right = false;

				Alert.alert(MessageType.WARNING, "Not permited action!", window);
			}
			else{

				((MemGameButtonData)buttonTwo.getButtonData()).setButtonURL();

				if ( ((MemGameButtonData)buttonOne.getButtonData()).getButtonURL().equals(
						((MemGameButtonData)buttonTwo.getButtonData()).getButtonURL())){

					right = true;

					buttonOne.setEnabled( false );
					buttonTwo.setEnabled( false );
				}

				else{
					right = false;
				}

				firstClick = !firstClick;
			}
		}

	}
}

Mensagens dinâmicas com MessageFormat

Esta é uma rápida dica sobre uma classe muito útil da API Java, a classe MessageFormat. Não vou dar muitas explicações, e mais detalhes podem ser encontrados na própria API: http://download.oracle.com/javase/1.4.2/docs/api/java/text/MessageFormat.html

Dentre as muitas coisas legais que esta classe permite fazer (formatação de valores monetários, data, hora), temos uma maneira de tratar mensagens internacionalizadas (ou não) cujo conteúdo muda de acordo com o valor específico de um parâmetro. Explico melhor:

Digamos que você precise internacionalizar, ou apenas armazenar em um properties, a seguinte frase:

“Duke, você nos visitou uma vez.”

Digamos que você precise tratar esta frase, de maneira que quando a quantidade de visitas for maior, ela mude para:

“Duke, você nos visitou (n) vezes”

Certo! O tal (n) alí pode ser qualquer número, então o que muita gente faz são concatenações para conseguir o resultado esperado, por exemplo:

“Duke, você nos visitou” + n + “vezes”;

Vamos Piorar a situação? E se o Duke nunca lhe visitou, ou seja, n é igual a zero? E é lógico que não é somente o Duke que lhe visita, então você ainda tem o seguinte:

m*, você nos visitou uma vez. (quando n = 1)

m* + você nos visitou + n + vezes. (quando n > 1)

m*, você nunca nos visitou. (quando n = 0)

*onde m pode ser o Duke ou qualquer pessoa.

Isto ainda pode ficar mais complicado quando você precisa passar a mensagem para mais de um idioma, pois a posição das partes variantes (m e n) pode ser diferente na mensagem. Este cenário é comum em sistemas internacionalizados.

Apesar de a maioria dos frameworks web com foco em componentes de apresentação apresentarem algum suporte a este tipo de situação, algumas vezes será necessário tratar mensagens assim no código Java.

Para estas situações a classe MessageFormat é ideal.

Em primeiro lugar, podemos ter apenas uma mensagem que trata todas as possibilidades, acredite. Basta colocarmos, para cada parte variável da mensagem, um placeholder, que identificará a presença de um valor dinâmico. Como no exemplo temos dois valores variáveis (n e m) teremos dois placeholders na mensagem. A sintaxe de um placeholder é um numérico entre chaves: {0} e {1}. Os valores indicam a posição dos parâmetros passados para o método que formatará a mensagem.

Nossa mensagem então ficaria assim:

{1}, você nos visitou {0} vezes.

Mas espere! Isto ainda não nos atende. Apenas uma das variações da mensagem está contemplada; precisamos do tipo de formato choice: este tipo de formato nos permite escolher dinamicamente uma parte da mensagem de acordo com o valor passado a um placeholder:

{1}, você {0,choice,0# nunca nos visitou.|1# nos visitou uma vez.|1<nos visitou {0,number,integer} vezes}.

Na mensagem acima, está em destaque a sintaxe do tipo de formatação choice; ele é colocado após o número do placeholder, depois de uma vírgula.

Em vermelho está o texto que será apresentado caso o valor passado ao placeholder seja zero. Em verde está o texto que será apresentado caso o valor passado ao placeholder seja um. Em azul está o texto que será apresentado caso o valor passado ao placeholder seja maior que um.

OK, mas como fazer a mágica da transformação? Basta passar a mensagem e os parâmetros para o método format, da classe MessageFormat. Abaixo coloco uma classe de exemplo, que mostra ainda a mesma mensagem traduzida em inglês e espanhol, para demonstrar que a localização dos placeholders pode mudar sem afetar o resultado esperado.


import java.text.MessageFormat;

public class FormatacaoMensagem{

private static String ingles = "You have {0,choice,0#never visited us|1#visited us once|1<visited us {0,number,integer} times}, {1}.";
private static String portugues = "{1}, você {0,choice,0#nunca nos visitou.|1#nos visitou uma vez.|1<nos visitou {0,number,integer} vezes}.";
private static String espanhol = "Usted {0,choice,0#nunca nos ha visitado, {1}|1#nos ha visitado una vez, {1}|1<nos ha visitado {0,number,integer} veces, {1}}.";

public static void main(String[] args){

Object[] nenhum = {
new Integer(0),
"Duke"

};

Object[] um = {
new Integer(1),
"Duke"

};

Object[] varios = {
new Integer(2),
"Duke"

};

System.out.println( MessageFormat.format(ingles, nenhum) );
System.out.println( MessageFormat.format(ingles, um) );
System.out.println( MessageFormat.format(ingles, varios) );

System.out.println( MessageFormat.format(portugues, nenhum) );
System.out.println( MessageFormat.format(portugues, um) );
System.out.println( MessageFormat.format(portugues, varios) );

System.out.println( MessageFormat.format(espanhol, nenhum) );
System.out.println( MessageFormat.format(espanhol, um) );
System.out.println( MessageFormat.format(espanhol, varios) );

}
}

Eis o resultado final:

Vale a pena conferir a API para as outras opções que a classe oferece.

Está dada a dica!

Dica: Prototipação com Balsamiq

Esta é uma dica rápida, de uma ferramenta que veio ao encontro de minhas necessidades, e pode ser o que você também procura.

Há algum tempo deixei de usar o iPlotz, e hoje uma colega de trabalho me indicou o Balsamiq.

Não vou explicar muito, apenas colocar uma imagem de uma tela e deixar o link para quem tiver interesse.

 

 

A instalação é fácil, mas é preciso ter instalada a plataforma AIR da Adobe (o que também é fácil conseguir – há um link na página de instalação para se chegar na instalação do AIR).

Eis a página do projeto: http://balsamiq.com/

 

Está dada a dica!