Skip to content

Silveira Neto Posts

Parsing a XML Sandwich with JavaFX

delicious sandwich

Let sandwich.xml be a file at /tmp directory with the content above.




   
   
   
   
   

We can open it using java.io.FileInputStream and so use it on a javafx.data.pull.PullParser. A PullParser is a event oriented parser that works with XML and YAML files. Above a general and simple parser with a GUI that show the list of events during the parse process.

import java.io.FileInputStream;
import javafx.data.pull.Event;
import javafx.data.pull.PullParser;
import javafx.ext.swing.SwingList;
import javafx.ext.swing.SwingListItem;
import javafx.scene.Scene;
import javafx.stage.Stage;

var list = SwingList { width: 600 height: 300 }

var myparser = PullParser {
   documentType: PullParser.XML;
   onEvent: function (e: Event) {
      var item = SwingListItem {
         text: "event {e}"
      };
      insert item into list.items;
   }
   input: new FileInputStream("/tmp/sandwich.xml");
}
myparser.parse();

Stage {
   title: "XML Sandwich"
   scene: Scene { content: list }
}

javafx xml sandwich

The XML cheese element produce two the outputs.

type:1 typeName:START_ELEMENT level:1 qname:cheese text:” namespaces:{} attributes:{type=chedar}
type:2 typeName:END_ELEMENT level:1 qname:cheese text:” namespaces:{} attributes:{type=chedar}

Notice that white spaces like tab and escape characters like new line also produced events from type TEXT. We are not interested on them. Above a parser that looks only those events of type START_ELEMENT or END_ELEMENT, look into it’s contents, building a sandwich at runtime based on the XML file.

import java.io.FileInputStream;
import javafx.data.pull.Event;
import javafx.data.pull.PullParser;
import javafx.data.xml.QName;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
import javafx.scene.Scene;
import javafx.stage.Stage;

// my sandwich starts as an empty VBox
var mysandwich = VBox {}

// give a name and returns a ImageView with a png image like the name
function ingredient(name){
   return ImageView {
      image: Image {
         url: "{__DIR__}{name}.png"
      }
   }
}

// basicaly, look the event and put a ingredient at mysandwich
var myparser = PullParser {
   documentType: PullParser.XML;
   onEvent: function (e: Event) {
      // starter xml elements
      if(e.type == PullParser.START_ELEMENT){
         // bread
         if(e.qname.name.equals("bread")){
             insert ingredient("bread_top") into mysandwich.content;
         }

         // hamburguer
         if(e.qname.name.equals("hamburguer")){
             insert ingredient("hamburguer") into mysandwich.content;
         }

         // catchup
         if(e.qname.name.equals("catchup")){
             insert ingredient("catchup") into mysandwich.content;
         }

         // maionese
         if(e.qname.name.equals("maionese")){
             insert ingredient("maionese") into mysandwich.content;
         }

         // lettuce
         if(e.qname.name.equals("lettuce")){
             insert ingredient("lettuce") into mysandwich.content;
         }

         // cheese
         if(e.qname.name.equals("cheese")){
            var type= e.getAttributeValue(QName{name:"type"});
            if(type.equals("cheedar")){
                 insert ingredient("cheedar") into mysandwich.content;
            } else {
                insert ingredient("cheese") into mysandwich.content;
            }
         }
      }

      // ending xml elements (just bread)
      if(e.type == PullParser.END_ELEMENT){
         if(e.qname.name.equals("bread")){
            insert ingredient("bread_botton") into mysandwich.content;
         }
      }
   }
   input: new FileInputStream("/tmp/sandwich.xml");
}
myparser.parse();

Stage {
   title: "XML Sandwich"
   scene: Scene {
      height: 300
      content: mysandwich
   }
}

Here’s our sandwich.

sandwich javaFX

Just changing the XML file you got a new sandwich.




   
   
   
   
   
   
   

double burguer

Bon appétit.

For more details on XML and JSON parsing see the JavaFX API.

Pizza Party!

duke pizza

A pizza party que o CEJUG ganhou por ter atingido mais de 100 usuários no OSUM em apenas dois dias é essa sexta-feira, às 18hrs no restaurante Florence L’Escale. As pizzas ficam por nossa conta e as bebidas por conta dos participantes.

Se você vai:

Lembre-se, é nessa sexta-feira, dia 26 de Dezembro, às 18 horas. Preencha o espaço na sua agenda entre as festas de natal e a farra de ano novo. ;D Maiores detalhes no site do evento dentro do OSUM.

JavaFX Santa’s Hat

Happy holidays in a JavaFX Style.

Try online

It’s a application to put a Santa’s hat on a picture from web. To test it obviously I used my picture with John Hall “Maddog”, the closest person I know to Santa Claus. ;D

I tried the approach from Chris Campbell’s Effect Playground application but I needed get both photo image and hat at the same time. In this application I used the Jean-Francois Screenshot Maker approach, taking a screenshot of the desired area. Maybe not the best solution, but it works very well. I also used his Screencapture.java and Util.java codes.

package santahat;

import javafx.ext.swing.SwingTextField;
import javafx.scene.CustomNode;
import javafx.scene.input.MouseEvent;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
import javafx.scene.layout.HBox;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.ext.swing.SwingButton;

var imgview = ImageView {
   image: Image {
      url: "{__DIR__}help.png"
   }
}

var txtfield:SwingTextField = SwingTextField {
   columns: 45
   text: "your image url"
   editable: true
   action: function(){
      println(txtfield.text);
      imgview.image = Image {
         url: txtfield.text;
      }
   }
};

var santahat:ImageView = ImageView {
   x: 240 y: -5
   var startX = 0.0
   var startY = 0.0
   var   zoom = 1.0
   var  angle = 0.0

   scaleX: bind zoom
   scaleY: bind zoom
   rotate: bind angle

   onMousePressed: function( e: MouseEvent ):Void {
      startX = e.sceneX - santahat.translateX;
      startY = e.sceneY - santahat.translateY;
   }

   onMouseDragged: function( e: MouseEvent ):Void {
      santahat.translateX = e.sceneX - startX;
      santahat.translateY = e.sceneY - startY;
   }

   onMouseWheelMoved: function( e: MouseEvent ):Void {
      if(e.controlDown) {
         angle += 
         e.wheelRotation * 10;
      } else {
         zoom += 
         e.wheelRotation / 20;
      }
   }

   image: Image {
      url: "{__DIR__}santahat.png"
   }
}

// Based on the Jean's ScreenshotMaker
// http://javafx.com/samples/ScreenshotMaker/index.html
var stage:Stage = Stage {
   title: "Santa Hat"
   width: 510 height: 480
   scene: Scene {
      content: [
         VBox {
            content: [ txtfield, imgview,
               SwingButton {
                  text: "Save"
                  action: function() {
                     // Ugly. See the entire source at through the link in the blog post
                  }
               }]
         }, santahat]
   }
}

Downloads:

Gravatar with JavaFX

Gravatar is easy way to put global recognized avatar images into any Internet application. Gravatar would stands for globally recognized avatar.

Below,  the Java class that I got from the Gravatar Java reference. Here is a static class called md5 that applies a MD5Sum algorithm over a string. Is a little complex code but all behavior keeps encapsulated and who uses it don’t need to know how it works. Just gives a string and receives a encrypted string. Those two codes are also a good example of how calling Java classes inside a JavaFX code.

package gravatarexample;

import java.security.MessageDigest;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;

public class MD5 {
   public static String toHex(String message) {
      try {
         MessageDigest md = MessageDigest.getInstance("MD5");
         byte array[] = md.digest(message.getBytes("CP1252"));
         StringBuffer sb = new StringBuffer();
         for (int i = 0; i < array.length; ++i) {
            sb.append(Integer.toHexString((array[i]&0xFF)|0x100).substring(1, 3));
         }
         return sb.toString();
      } catch (NoSuchAlgorithmException e) {
      } catch (UnsupportedEncodingException e) {
      }
      return null;
   }
}

As a Java class in the same package, any JavaFX (or Java) code can call it without any problem. Just to keep the code more clear I’m importing it explicitly. Is this example I also create some Swing interface to give user the option to put his mail, adjust the image size and get a output direct link or html image tag.

package gravatarexample;

import gravatarexample.MD5;
import javafx.ext.swing.SwingButton;
import javafx.ext.swing.SwingSlider;
import javafx.ext.swing.SwingTextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
import javafx.scene.Scene;
import javafx.stage.Stage;

var mail = "Email";
var key = "";

function gravatalize(mail:String, size: Integer): String {
   return "http://www.gravatar.com/avatar/{MD5.toHex(mail)}?s={size}"
}

var inputtxt = SwingTextField {
   columns: 20
   text: mail
}

var slider = SwingSlider {
   minimum: 10
   maximum: 128
   value: 100
   vertical: false
}

var button = SwingButton {
   text: "Get Gravatar"
   action: function() {
      key = gravatalize(inputtxt.text, slider.value);
      directoutput.text = key;
      htmloutput.text = "\"gravatar\"";
      photo.image = Image {
         backgroundLoading: true,
         url: key};
   }
}

var photo:ImageView = ImageView {
   image: null
}

var directoutput = SwingTextField {
   columns: 20
   text: "direct link image"

}

var htmloutput = SwingTextField {
   columns: 20
   text: "html tag image"
}

Stage {
   title: "Gravatar"
   width: 300
   height: 340
   scene: Scene {
      content: [
         VBox {
            spacing: 10
            content: [inputtxt, slider, button, directoutput, htmloutput, photo]
         },
      ]
   }
}

The string itself is assembled in the gravatalize function. You give a mail and it’s returns a Gravatar direct link to the image. There’s many cool ways to use together Gravatar and a JavaFX Internet application.

JavaFX, Simple Tile Set

Tile sets are a very simple way to draw scenarios with repeated elements. From simple to complex ones using a very low footprint.

First step, load the png file that stores the tileset into a Image. The file tiles.png shoud be in the same directory of the source code. I adjusted some tiles from those tile set I’ve blogged here before into a grid of 10×10 tiles.

Set of tiles, example

var tileset = Image {
   url: "{__DIR__}tiles.png"
}

Notice that each tile have 32 of height and 32 of width. We will assume this and use theses numbers when performing calculations to find a single tile in our tile set.

def w = 32;
def h = 32;

To display a Image in the screen we use a ImageView node. A ImageView can have a viewport property to create crop or zoom effect. A viewport is just a Rectangle2D, a object with position (minX and minY), height and width. If we want to display the first tile in the tileset we do

first tile

ImageView {
   image: tileset
   viewport: Rectangle2D{
      minX: 0, minY: 0, height: 32, width: 32
   }
}

Notice that the minX determines the column and minY the row in the tileset. The first row is 0*32, the second row is 1*32 and so on. If we want to display the tile at the second line and third column of the tileset we do

another_tile

ImageView {
   image: tileset
   viewport: Rectangle2D{
      minX: 2 * 32 , minY: 1*32, height: 32, width: 32
   }
}

Those properties in a Rectangle2D are for init and read only. So I created a list with all Rectangles I can need for use as a viewport.

def viewports = for (row in [0..9]) {
   for (col in [0..9]) {
       Rectangle2D{
           minX: col * w, minY: row * h, height: w, width: h
       }
   }
}

The scenario map is stored in another list. The first element of the list is 7, that is, the first tile in the scenario is the 7th tile from the tile set.

var map = [
    7,  3,  3,  3,  3,  3,  3,  3,  3,  8,
   19, 26, 40, 41, 24, 13, 13, 23, 24, 19,
   19, 36, 50, 51, 34,  2,  2,  2, 34, 19,
   19,  2,  2,  2,  2,  2,  2,  2, 25, 19,
   19, 57, 58, 44, 45, 46,  2,  2, 35, 19,
   27,  3,  3,  6, 55, 56,  5,  3,  3, 38,
   19, 60, 13, 16, 47, 48, 15, 13, 61, 19,
   19, 70,  1, 33,  1,  1,  1,  1, 71, 19,
   19,  1,  1,  1,  1,  1,  1,  1, 49, 19,
   17,  9,  9,  9,  9,  9,  9,  9,  9, 18,
];

Finally to create a scenario with 100 tiles, 10 per row and with 10 rows, in a list called tiles. Each iteration of this loop creates a ImageView. Each ImageView will store a single tile. We get the tile number in the map list and so use it to index the viewports list.

var tiles =  for (row in [0..9]) {
   for (col in [0..9]) {
      ImageView {
         x: col * w, y: row * h,
         viewport: bind viewports[map[row * 10 + col]]
         image: tileset
      }
   }
}

Additionally I added two things to transform this program also in a (extremely)  simple map editor. At each ImageView I added a callback for onMouseClicked event. When you click on a tile, it changes its map position, ie, the tile. The next tile for the left button and the last tile for any other button.

onMouseClicked: function( e: MouseEvent ):Void {
   var amount = if(e.button == MouseButton.PRIMARY) { 1 } else { -1 };
   map[row * 10 + col] = (map[row * 10 + col] + amount) mod 100;
}

The other thing is to print the map list when the program is over. There is the full program:

package tileeditor;

import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.image.Image;
import javafx.scene.CustomNode;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.geometry.Rectangle2D;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.MouseButton;

def w = 32;
def h = 32;

var map = [
    7,  3,  3,  3,  3,  3,  3,  3,  3,  8,
   19, 26, 40, 41, 24, 13, 13, 23, 24, 19,
   19, 36, 50, 51, 34,  2,  2,  2, 34, 19,
   19,  2,  2,  2,  2,  2,  2,  2, 25, 19,
   19, 57, 58, 44, 45, 46,  2,  2, 35, 19,
   27,  3,  3,  6, 55, 56,  5,  3,  3, 38,
   19, 60, 13, 16, 47, 48, 15, 13, 61, 19,
   19, 70,  1, 33,  1,  1,  1,  1, 71, 19,
   19,  1,  1,  1,  1,  1,  1,  1, 49, 19,
   17,  9,  9,  9,  9,  9,  9,  9,  9, 18,
];

var tileset = Image {
    url: "{__DIR__}tiles.png"
}

def viewports = for (row in [0..9]) {
   for (col in [0..9]) {
       Rectangle2D{
           minX: col * w, minY: row * h, height: w, width: h
       }
   }
}

var tiles =  for (row in [0..9]) {
   for (col in [0..9]) {
      ImageView {
         x: col * w, y: row * h,
         viewport: bind viewports[map[row * 10 + col]]
         image: tileset

         onMouseClicked: function( e: MouseEvent ):Void {
            var amount = if(e.button == MouseButton.PRIMARY) { 1 } else { -1 };
            map[row * 10 + col] = (map[row * 10 + col] + amount) mod 100;
         }
      }
   }
}

Stage {
    title: "JavaFX Simple Tile Editor"
    scene: Scene {
        content: [ tiles ]
    }
    onClose: function() {
        println(map);
    }
}

Here is the result for that map

tlemap javafx

And you can try it yourself in your browser. Play it online now.

Here is a video of it working

[youtube]lxuBEoItB5E[/youtube]

Downloads:

Possibilities

We are using just  a image that can handle 100 tiles, tiles.png with less than 30Kb. The map is also composed with 100 tiles. Each tile we can choose between 100 different tiles, so we can compose 10100 different maps (one googol10 ). Most of them are useless and without any sense, but some are cool. 🙂