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

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? ;-)
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

Expressões, preconceito e racismo

Expressões preconceituosas e racistas Antes de alguma outra frase, primeiro peço licença para falar de mais um assunto do qual não domino. Falo por acreditar que um leigo presta serviço maior ao debater assunto com base em fontes (ainda que seja uma Wikipedia) e no pensamento lógico do que simplesmente se manter mudo a questões do cotidiano. Em voga agora está em falar quais são ou eram as expressões preconceituosas e racistas que até a pouco eram toleradas em muitos meios.
Como é covarde dizer que em boca fechada não entra racismo. O racismo não é perpetrado apenas por quem profere mas por quem se cala à agressão perpetrada a outrem. Mas veremos que a questão é muito mais complexa que os cães raivosos do politicamente correto querem dizer.
Tomo aqui a palavra racista, como sendo algo usado para impor a dominação de uma “raça” sobre outra. Portanto, a acusação de racismo vai muito além da mera acusação de preconceito. Não tenho o menor apreso por vitimismo barato, onde expressões que…

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 leva …

Curry with JS

Partial application and currying with Javascript In the strict way, currying is the technique of transforming a function that takes multiple arguments (a tuple of arguments) to one function that receive only one. In such way, currying techniques allow transform one multi-parameter function in a chain of functions, each one with a single argument. Looks complicated? Blah.. it is not true.
In this little article, we are actually more interesting in partial applications. Let’s take the Mozilla Example for replace function in String. As we know, we can use a “replacer” function as paramenter for replace method in String object.
Let’s say that we want to split a String defined by a non-numerical part, a numerical part and finally a non-alphanumeric part. Here is how:
function replacer(match, p1, p2, p3, offset, string){ // p1 is nondigits, p2 digits, and p3 non-alphanumerics return [p1, p2, p3].join(' - '); }; We can try it as usual…
var newString = "abc12345#$*%".r…