Pular para o conteúdo principal

Javascripting in Java



Linguagens dinâmicas apresentam várias vantagens sobre as linguagens compiladas. Uma dessas vantagens é a habilidade de adicionar código em tempo de execução com facilidade. Por exemplo, imagine um servidor que de alguma forma receba e processe comandos. Num ambiente Java (onde o servidor é um programa Java assim como os comandos são objetos Java) é um certo malabarismo com o Classloader para fazer isso.

http://www.javaworld.com/javaworld/jw-06-2006/jw-0612-dynamic.html?page=2


Enquanto que esse dinamismo é ainda “sustentável” para Classes, a situação é mais complicada para fragmentos de código. Nesse caso é preciso compilar uma classe Java e carregá-la. Com Javascript, a situação é outra. Uma vez que a linguagem é interpretada, é simplesmente trivial adicionar fragmentos de código. Para provar isso vou implementar um programa que imprime gráficos, para funções arbitrárias de uma variável x “real” (aliás, radicalmente arbitrárias... não irei nem mesmo validar se a entrada é de fato uma função).

Inicialmente definimos uma interface para a função. Não queremos que o código cliente seja impregnado com detalhes técnicos do funcionamento do javax.script. Queremos definir a interface mais simples possível sem comprometer o desempenho da aplicação.
public interface Function {
  double[] function(double xInit, double xfinal, double step);

  public double function(double x);
 }

Agora faremos teste com uma função bem conhecida x^2 (ou f(x) = x*x). Aqui o teste unitário da função:

/**
  * Test of function method, of class RhinoCalc.
  */
 @Test
 public void functionXQuadrado() throws Exception {
  setFunction();
  double xInit = 0.0;
  double xfinal = 10.0;
  double step = 1;
  double[] result = function.function(xInit, xfinal, step);
  assertTrue(Arrays.equals(new double[] { 0, 1, 4, 9, 16, 25, 36, 49, 64,
    81, 0 }, result));
  for (int i = 0; i < result.length; i++) {
   System.out.println(result[i]);
  }
 }
Para implementar a classe vou seguir os passos mais simples para adicionar o Javascript. Isso se resume a carregar o motor de scripts, adicionar a função e realizar a chamada necessária. Para carregar o motor de scripts:
public RhinoFunction() {
  ScriptEngineManager mgr = new ScriptEngineManager();
  jsEngine = mgr.getEngineByName("JavaScript");
 }
Atribuir uma função arbitrária:
public void setFunction(String function) throws ScriptException {
  jsEngine.eval("function f(x) { \n" + " return  " + function + ";}");
  invocableEngine = (Invocable) jsEngine;
 }
e invocar uma função:
public double function(double x) {
  try {
   return (Double) invocableEngine.invokeFunction("f", x);
  } catch (Exception e) {
   Logger.getLogger(RhinoFunction.class.getName()).log(Level.SEVERE,
     null, e);
   throw new RuntimeException(e);
  }
 }

 public double[] function(double xInit, double xfinal, double step) {
  double deltaX = xfinal - xInit;
  int size = (int) Math.ceil(deltaX / step);
  double[] result = new double[size + 1];
  int i = 0;
  for (double x = xInit; x < xfinal; x += step, i++) {
   try {
    result[i] = (Double) invocableEngine.invokeFunction("f", x);
   } catch (Exception ex) {
    Logger.getLogger(RhinoFunction.class.getName()).log(
      Level.SEVERE, null, ex);
    throw new RuntimeException(ex);
   }
  }
  return result;
 }
Agora um teste que exige mais desempenho. Podemos utilizar a classe para gerar gráficos de funções. Aqui um modelo que resume o funcionamento dessas classes: Ao pressionar o botão “plot” o seguinte método atualiza o modelo do plano cartesiano e solicita a atualização do painel.
private void plotButtonActionPerformed(java.awt.event.ActionEvent evt) {
  CartesianPanel painel = (CartesianPanel) this.graphicPanel;
  try {
   rhinoFunction.setFunction(polinomioTextField.getText());
  } catch (ScriptException e) {
   e.printStackTrace();
   JOptionPane
    .showMessageDialog(
    this,
    "O interpretador não conseguiu interpretar a função escrita.",
    "Função Inválida", JOptionPane.ERROR_MESSAGE);
  }
  painel.setFunction(rhinoFunction);
  double initialX = Double.parseDouble(initialXTextField.getText());
  double finalX = Double.parseDouble(xFinalTextField.getText());
  double initialY = Double.parseDouble(initialYTextField.getText());
  double finalY = Double.parseDouble(finalYTextField.getText());
  painel.getCartesianPlan().setDimension(initialX, finalX, initialY, finalY);
  painel.repaint();
 }
Eu adicionei alguns métodos de “zoom” com o mouse de forma que o programa tivesse mais dinamismo. Essas funcionalidades foram implementadas tanto no painel do desenho que sofre a ação como numa classe especialista “adapter” para o mouse. Isso é praticamente um completo programa de “plot” extramente simples e compacto que ilustra as facilidades do JFC e javax.script. Outras Considerações Claro que o exemplo poderia ser estendido para compreender outros conceitos e funcionalidades facilmente implementadas com JFC (como a possibilidade de impressão do gráfico), a habilidade de desenhar retângulos com a área selecionada, a possibilidade de arrastar o gráfico deslocando o ponto inicial, etc. Mas isso vai longe: uma longa caminhada do ArcTest para o Kplot... http://java.sun.com/applets/jdk/1.4/demo/applets/ArcTest/example1.html http://edu.kde.org/kmplot/

Comentários

Unknown disse…
Legal ver um exemplo da JSR-223. Tenho umas perguntas:

1 - O q vc tem contra o <pre>?
2 - Nessa linha:
CartesianPanel painel = (CartesianPanel) this.graphicPanel;
Pq o cast?
3 - Tem algum easter egg? ;-)
Rodrigo Lopes disse…
4) Por que o blogger eu não enviou sua dúvida por email?
...
Respondendo
1) Acho que o pre não fica bom, não sei. Queria que ficasse colorido como no eclipse.
2) Essa é fácil:
Se eu não usasse o cast não poderia fazer isso depois:
painel.setFunction(rhinoFunction);
3) Ainda não tenho easter egg. :(

Postagens mais visitadas deste blog

Um texto pós-moderno - better man

Espere olhando para as horas... são 4 horas. Tem que parar. Nesse tom melancólico, começa a modesta música "better man", uma balada pop composta por Eddie Vedder ainda na adolescência. A música é a ilustração perfeita da ironia. O próprio título é irônico, uma vez que em momento algum na música aparece um better man. She lies and says she's in love with him, can't find a better man... Irônico, não!? Para começar, com a personagem central da história, a mulher que aguarda tarde da noite seu esposo... Ela chega a treinar com o espelho o fim do relacionamento. E o que faz? Diz a negação do que queria dizer. Vedder escreve músicas sobre sentimentos fortes. Sua relação com a mãe foi bastante complicada pelo o que descreve em suas canções. Na trilogia Mommy, Vedder descreve um homem perturbado com o relacionamento materno; a mãe mente para o filho sobre a identidade do pai, revela a verdade para o garoto na puberdade dizendo a ele como se parece com o verdadeiro pai e o

Pequeno manual do ócio em terras alemãs

  Pequeno manual do ócio em terras alemãs Como Lei alemã favorece aproveitadoras (e alguns aproveitadores que nunca tive o desprazer de conhecer)   Há algumas vias pelas quais pessoas de países em desenvolvimento migram para países como a Alemanha.   Por exemplo, é sabido que países desenvolvidos sofrem de escassez de mão-de-obra qualificada. Por esse motivo, países como a Alemanha dispõe vistos "especiais" para profissionais em demanda. Esse é o conceito do Blaukart (Blue Card) que na Alemanha se destina a profissionais salário anual seja superior a 55 mil euros ou 43 mil no caso de profissionais de áreas em alta demanda. Não há como recrutar essa mão-de-obra sem que a família desses profissionais também possa ser relocada. Então esses profissionais e seus familiares são relocados.   Além de se qualificar para essas vagas em demanda, ou ser parte direta da família qualificada, outra via possível para a imigração para o território alemão é através do matrimôni

O argumento anti-álcool

A lógica contra a produção do álcool é mais ou menos a seguinte: Os produtores capitalistas, produtores do combustível de humanos e máquinas irão preferir vender combustível mais caro para os mais ricos do que comida barata para os mais pobres. Máquinas e homens irão competir por combustível... Mas enquanto os ricos terão dinheiro para comprar comida e combustível o que sobrará aos pobres!? Vale lembrar que não importa se a produção é de cana ou de milho, a competição é pela terra e não pelo grão. Ainda, mesmo que o país agrícola taxe o produtor de combustível de maneira diferenciada ao produtor de comida, o governo teria maiores dificuldades em repartir o "bolo", haja vista que os governos que temos não são as instituições mais eficientes e, além do que, a comida estará mais cara. Ora, esquecem os "amigos" comunistas que a venda de biocombustível dará aos países agrícolas uma oportunidade ímpar de participar da economia mundial como protagonistas, e não meros fi