Using JSDoc

Enhancing Javascript with the Help of JSDoc and JSDoc-checking IDEs

JSDoc is a way of documentating Javascript, much like JavaDoc for Java. JSDoc does pretty much the same thing. It documents and annotates Javascript elements. However given that javascript has much looser language rules than Java, these annotations can be leveraged to tighten parts of the code that may appear ambiguous particularly in a large project.

Probably the most useful features of JSDoc is the ability to sprinkle annotations amongst Javascript code which can then give semantic meaning to a function or variable. Using an advanced Javascript IDE, these annotations can be interpreted at compile time, and provide a kind of compile time checking, while also aiding in code-completion.

As of writing, the only IDE that can interpret JSDoc to any reasonable level is Webstorm. So all examples in this article are using the Webstorm IDE.

Simple Type Checking

Parameters types can be defined in the comments of the function. In this example we are annotating the x value being passed into the popup() method.

 

var v = popup("hey, yo, whats up!");
/**
 * @param {string} x message
 */
function popup(x) {
    alert(x);
}

If we were to pass in a number to our popup() method, a conflict should occur with our JSDoc.

 

var three = popup(123456);
/**
 * @param {string} x message
 */
function popup(x) {
    alert(x);
}

If we look at this in Webstorm, we should see a squiggly line underneath the number 12345 that we are trying to pass in as a string.

jsdoc2

 

If we hover our mouse over the squiggly line, we should see :

jsdoc3

We can perform this type of annotation on the primitive types number and Boolean.

Arrays and the Type Annotation

Javascript types also includes Arrays and Objects too.

In the following example we are passing an array into a function :

 

function Banana() {}
function Apple() {}

var appleArray = [];
appleArray.push(new Apple());
appleArray.push(new Apple());

processBananaArray(appleArray);  //Webstorm however does not recognise this as an error

var bananaArray = [];
bananaArray.push(new Banana());
bananaArray.push(new Banana());

processBananaArray(bananaArray);

/**
 * @param {Array.} array my array of bananas
 */
function processBananaArray(array) {

    array.push(new Banana());

    /** @type {Banana} **/
    var elem1 = array[1]; // :( 

    /** @type {Banana} **/
    var elem2 = array.pop(); //Webstorm however does not recognise this as an error
}

We create two types of fruit, and make an array for each of them. By using angular brackets, we can define the type of the array being passed in (Array.<Apple> )

Using the @type annotation we can annotate a variable. In our array example we are stating that we want out element to be of type Banana. As our array being passed in is of type Apple, our JSDoc annotation is being infringed.

Parameter annotations on arrays can also help in code completion. The example below understands that the element in an array is an Apple and shows the Apple elements in the drop down list.

jsdoc4

Object Literals

Javascript objects can be very arbitrary objects. There is no real sense of type .

In the following example we have the @param tag annotation. Here we are annotating an object literal with two fields: ‘name’, and ‘email’ which are both strings :

 

var user = {name:"jane", email:"blah"};
processUser3(user); // :) 

/**
 * @param {name:string, email:string} user
 */
function processUser3(user) {

    /**@type string **/
    var i = user.name; //  :) 
}

The IDE has no problem with this. In the method body, a variable is assigned to the user.name. This variable has to be of type string, and matches up with the name type being passed into the method.

The following two exmples show where the IDE would complain :

 
var user = {name:"jane", email:"blah"};
processUser(user); // :( 

/**
 * @param {number} user
 */
function processUser(user) {
    /**@type number **/
    var i = user.name; //  :( 
}

In this case we are trying to treat one of the string fields as a number :

 

var user = {name:"jane", email:"blah"};
processUser2(user); // :) 

/**
 * @param {name:string, email:string} user
 */
function processUser2(user) {

    /**@type number **/
    var i = user.name; //  :( 
}

We can even document an array of literals.

 
var v =  [
    {
        "name": "c917379",
        "email": "jim@bmw.de"
    },
    {
        "name": "c917389",
        "email": "jane@bmw.de"
    }
]

doArrayOfLiterals(v);

/**
 * @param {{name:string, email:string}[]} param
 */
function doArrayOfLiterals(param) {

}

In the following example the IDE will complain about the variable i being assigned to a string :

 

var v =  [
    {
        "name": "c917379",
        "email": "jim@bmw.de"
    },
    {
        "name": "c917389",
        "email": "jane@bmw.de"
    }
]

doArrayOfLiterals(v);

/**
 * @param {{name:string, email:string}[]} param
 */
function doArrayOfLiterals(param) {

    /** @type {number} **/
    var i = param[1].email;

}

Again, code completion is enhanced. The IDE deals with the fields in an object literal :

jsdoc5

 

Return Types

Returns can be annotated too.

In this example the IDE indicates there is a problem on the line where x is assigned the return value.

 
/** @type string **/
var x = returnSomething(); // :( 
/**
* @returns {number}
*/
function returnSomething() {
return 1;
}

 

In this example the assignment is correct, however there is now a problem with the return statement. The IDE picks up on what is happening inside the function is incorrect.

   

/** @type string **/
var x = returnSomething2(); // :) 
/**
* @returns {string}
*/
function returnSomething2() {
return 1; // :( 
}

Finally this example has everything working correctly :

   
/** @type string **/
var x = returnSomething3(); // :) 
/**
*
* @returns {string}
*/
function returnSomething3() {
return "hi"; // :) 
}

Mixed Type

Because Javascript is duck typed, methods can accepted objects of various types. Sometimes you want to document a function and tell it that it can only accept objects of these ‘types’.

   

function Banana() {
}
function Apple() {
}
function Orange() {
}

processFruit(new Banana());
processFruit(new Apple());
processFruit(new Orange());

/**
*
* @param {Banana | Apple} fruit my array of bananas
*/
function processFruit(fruit) {

}

The IDE will complain at exactly this line : processFruit(new Orange())

Wrapping up..

I hope this introduction to JSDoc was useful. I don’t believe in strictly enforcing JSDoc across a whole project, but I do feel that JSDoc with a JSDoc-checking IDE becomes necessary as the project grows bigger. It allows one to treat javascript functions as black boxes, and prevents lots of picking around inside a function to try and understand what is going wrong.

This is particularly useful when you have Javascript project modules, ie. Common projects that are used across other javascript projects. A typical real world implementation I have come across is a table component that is used across many projects. It has a setModel(..) method, where the table model is passed into the component (Just like in Swing J ). Using a JSDoc-checking IDE, the developer would be notified that this object being passed in requires an array of columns, an array of entities, and a method getValueAt among other things.


Posted in JavaScript | Tagged , , , | Leave a comment

Bar Chart in Java Swing

Update !

This bar chart compoment has now been moved into the Iceberg Charts project . Please use Iceberg Charts for a more complete charting experience.

I’ve created a little Java component which creates bar graphs. It only displays bars along the Y axis, however the component is very versatile and configurable. You can change its size, fonts, ticks on the Y-axis, and bar width.

Here are some examples of using it :

bar1

    
ArrayList values = new ArrayList();
values.add(new Bar(90, Color.RED, "Apple"));
values.add(new Bar(14, Color.BLUE, "Banana"));
values.add(new Bar(67, Color.GREEN, "Plum"));
values.add(new Bar(30, Color.ORANGE, "Radish"));
values.add(new Bar(10, Color.YELLOW, "Corn"));

int primaryIncrements = 20; 
int secondaryIncrements = 10; 
int tertiaryIncrements = 5;
Axis yAxis = new Axis(100, 0, primaryIncrements, secondaryIncrements, 
                     tertiaryIncrements, "Number of Fruits");

BarChart barChart = new BarChart(values, yAxis);

bar2

    
		ArrayList values = new ArrayList();
		values.add(new Bar(10, Color.BLACK, "1"));
		values.add(new Bar(14, Color.BLUE, "2"));
		values.add(new Bar(300, Color.BLACK, "3"));
		values.add(new Bar(12, Color.BLUE, "4"));
		values.add(new Bar(18, Color.BLACK, "5"));
		values.add(new Bar(19, Color.BLUE, "6"));
		values.add(new Bar(23, Color.BLACK, "7"));
		values.add(new Bar(33, Color.BLUE, "8"));
		values.add(new Bar(11, Color.BLACK, "9"));
		values.add(new Bar(33, Color.BLUE, "10"));
		values.add(new Bar(11, Color.BLACK, "11"));
		values.add(new Bar(33, Color.BLUE, "12"));
		values.add(new Bar(11, Color.BLACK, "13"));
		values.add(new Bar(88, Color.BLUE, "14"));
		values.add(new Bar(5, Color.BLACK, "15"));

		Axis yAxis2 = new Axis(500, 0, 50, 0, 0, "Numbers");
		BarChart barChart2 = new BarChart(values, yAxis2);
		barChart2.topOffset = 160;
		barChart2.xFont = new Font("Blackadder ITC", Font.PLAIN, 20);
		barChart2.yFont = new Font("Blackadder ITC", Font.PLAIN, 20);
		barChart2.xAxis = "Playing with fonts";

		barChart2.xCatFont = new Font("Blackadder ITC", Font.PLAIN, 14);
		barChart2.yCatFont = new Font("Blackadder ITC", Font.PLAIN, 16);

		barChart2.titleFont = new Font("Ravie", Font.PLAIN, 70);
		barChart2.title = "Really Big Text";
		barChart2.width = 900;

bar3

          

ArrayList values = new ArrayList();
values.add(new Bar(40, Color.PINK, "Mini"));
values.add(new Bar(30, Color.CYAN, "Smart Car"));
values.add(new Bar(80, Color.ORANGE, "Beetle"));

Axis yAxis3 = new Axis(100, 0, 50, 20, 5, "Cuteness Factor");
BarChart barChart3 = new BarChart(values, yAxis3);

barChart3.width = 240;
barChart3.height = 200;
barChart3.leftOffset = 60;
barChart3.rightOffset = 60;
barChart3.topOffset = 60;
barChart3.bottomOffset = 60;
barChart3.barWidth = 30;

barChart3.xLabelOffset = 20;

barChart3.title = "Small Cars";
barChart3.xAxis = "Small Thing";
barChart3.xCatFont = new Font("Ariel", Font.PLAIN, 6);

bar4

           

ArrayList values = new ArrayList();

for (int i = 0; i < 365; i++) {
     double d = Math.random();
     values.add(new Bar((int)(100 * d), Color.GRAY, ""));
}

Axis yAxis4 = new Axis(100, 0, 50, 10, 1, "Percent Sunlight");
BarChart barChart4 = new BarChart(values, yAxis4);

barChart4.width = 1000;
barChart4.xAxis = "Day of Year";
barChart4.titleFont = new Font("Ariel", Font.PLAIN, 24);
barChart4.title = "Annual Sunlight Variability";

barChart4.barWidth = 1;

The code is below. It’s basically three classes: BarChart, Bar and Axis.
A few things to note :

  • It is highly customizable. All the package level fields in BarChart can modified.
  • The Y-Axis can have up to 3 categories of ticks. In the Axis constructor, you just need to set the second and third to zero if you don’t want to used them (See my examples).
  • It is geometrically relative, however you can have problems with it. The key things to know are width and height, and leftOffset, rightOffset, topOffset and bottomOffset. The offsets need to be big enough to accomodate a title with a very large font (again see my examples).

Please let me know if this component has been useful to you. I spent a lot of time creating it, so a little feedback would be very important to me 

    
/**
 * @copyright 2014 
 * @author Oliver Watkins (www.blue-walrus.com) 
 * 
 * All Rights Reserved
 */
package com.bluewalrus;

import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.util.ArrayList;
import javax.swing.JPanel;

/**
 * @copyright 
 * @author Oliver Watkins 
 * 
 * (www.blue-walrus.com) All Rights Reserved
 */
public class BarChart extends JPanel {

    //offsets (padding of actual chart to its border)
    int leftOffset = 140;
    int topOffset = 120;
    int bottomOffset = 100;
    int rightOffset = 15;

    //height of X labels (must be significantly smaller than bottomOffset)
    int xLabelOffset = 40; 
    //width of Y labels (must be significantly smaller than leftOffset)
    int yLabelOffset = 40; 

    //tick widths
    int majorTickWidth = 10;
    int secTickWidth = 5;
    int minorTickWidth = 2;

    String xAxis = "X Axis";
    String yAxisStr = "Y Axis";
    String title = "My Fruits";

    int width = 500; //total width of the component
    int height = 430; //total height of the component

    Color textColor = Color.BLACK;
    Color backgroundColor = Color.WHITE;

    Font textFont = new Font("Arial", Font.BOLD, 20);
    Font yFont = new Font("Arial", Font.PLAIN, 12);
    Font xFont = new Font("Arial", Font.BOLD, 12);
    Font titleFont = new Font("Arial", Font.BOLD, 18);

    Font yCatFont = new Font("Arial", Font.BOLD, 12);
    Font xCatFont = new Font("Arial", Font.BOLD, 12);

    ArrayList bars;
    Axis yAxis;
    int barWidth = 10;

    /**
     * Construct BarChart
     * 
     * @param bars a number of bars to display
     * @param yAxis Axis object describes how to display y Axis 
     */
    BarChart(ArrayList bars, Axis yAxis) {
        this.bars = bars;
        this.yAxis = yAxis;
        this.yAxisStr = yAxis.yLabel;
    }

    @Override
    protected void paintComponent(Graphics g) {

        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, // Anti-alias!
                RenderingHints.VALUE_ANTIALIAS_ON);

        g.drawRect(0, 0, width, height);
        g2d.setColor(backgroundColor);
        g.fillRect(0, 0, width, height);
        g2d.setColor(Color.BLACK);

        int heightChart = height - (topOffset + bottomOffset);
        int widthChart = width - (leftOffset + rightOffset);

        //left
        g.drawLine(leftOffset, topOffset, leftOffset, heightChart + topOffset);

        //bottom
        g.drawLine(leftOffset, heightChart + topOffset, leftOffset + widthChart, heightChart + topOffset);

        if (this.yAxis.primaryIncrements != 0)
            drawTick(heightChart, this.yAxis.primaryIncrements, g, Color.BLACK, majorTickWidth);
        if (this.yAxis.secondaryIncrements != 0)
            drawTick(heightChart, this.yAxis.secondaryIncrements, g, Color.BLACK, secTickWidth);
        if (this.yAxis.tertiaryIncrements != 0)
            drawTick(heightChart, this.yAxis.tertiaryIncrements, g, Color.BLACK, minorTickWidth);

        drawYLabels(heightChart, this.yAxis.primaryIncrements, g, Color.BLACK);

        drawBars(heightChart, widthChart, g);

        drawLabels(heightChart, widthChart, g);
    }

    private void drawTick(int heightChart, int increment, Graphics g, Color c, int tickWidth) {

        int incrementNo = yAxis.maxValue / increment;

        double factor = ((double) heightChart / (double) yAxis.maxValue);

        double incrementInPixel = (double) (increment * factor);

        g.setColor(c);

        for (int i = 0; i < incrementNo; i++) {
            int fromTop = heightChart + topOffset - (int) (i * incrementInPixel);
            g.drawLine(leftOffset, fromTop, leftOffset + tickWidth, fromTop);
        }
    }

    private void drawYLabels(int heightChart, int increment, Graphics g, Color c) {

        int incrementNo = yAxis.maxValue / increment;

        double factor = ((double) heightChart / (double) yAxis.maxValue);

        int incrementInPixel = (int) (increment * factor);

        g.setColor(c);
        FontMetrics fm = getFontMetrics(yCatFont);

        for (int i = 0; i < incrementNo; i++) {
            int fromTop = heightChart + topOffset - (i * incrementInPixel);

            String yLabel = "" + (i * increment);

            int widthStr = fm.stringWidth(yLabel);
            int heightStr = fm.getHeight();

            g.setFont(yCatFont);
            g.drawString(yLabel, (leftOffset - yLabelOffset) + (yLabelOffset/2 - widthStr/2), fromTop + (heightStr / 2));
        }
    }

    private void drawBars(int heightChart, int widthChart, Graphics g) {

        int i = 0;
        int barNumber = bars.size();

        int pointDistance = (int) (widthChart / (barNumber + 1));

        for (Bar bar : bars) {

            i++;

            double factor = ((double) heightChart / (double) yAxis.maxValue);

            int scaledBarHeight = (int) (bar.value * factor);

            int j = topOffset + heightChart - scaledBarHeight;

            g.setColor(bar.color);
            g.fillRect(leftOffset + (i * pointDistance) - (barWidth / 2), j, barWidth, scaledBarHeight);

            //draw tick
            g.drawLine(leftOffset + (i * pointDistance),
                    topOffset + heightChart,
                    leftOffset + (i * pointDistance),
                    topOffset + heightChart + 2);

            FontMetrics fm = getFontMetrics(xCatFont);
            int widthStr = fm.stringWidth(bar.name);
            int heightStr = fm.getHeight();

            g.setFont(xCatFont);
            g.setColor(Color.BLACK);

            int xPosition = leftOffset + (i * pointDistance) - (widthStr / 2);
            int yPosition = topOffset + heightChart + xLabelOffset - heightStr/2;

            //draw tick
            g.drawString(bar.name, xPosition, yPosition);
        }
    }

    private void drawLabels(int heightChart, int widthChart, Graphics g) {

        Graphics2D g2d = (Graphics2D)g;

        AffineTransform oldTransform = g2d.getTransform();

        FontMetrics fmY = getFontMetrics(yFont);
        int yAxisStringWidth = fmY.stringWidth(yAxisStr);
        int yAxisStringHeight = fmY.getHeight();

        FontMetrics fmX = getFontMetrics(xFont);
        int xAxisStringWidth = fmX.stringWidth(yAxisStr);
        int xAxisStringHeight = fmX.getHeight();

        FontMetrics fmT = getFontMetrics(titleFont);
        int titleStringWidth = fmT.stringWidth(title);
        int titleStringHeight = fmT.getHeight();

        g2d.setColor(Color.BLACK);
        //draw tick
        g2d.rotate(Math.toRadians(270)); //rotates to above out of screen.

        int translateDown = -leftOffset -(topOffset + heightChart/2 + yAxisStringWidth/2);

        //starts off being "topOffset" off, so subtract that first
        int translateLeft = -topOffset + (leftOffset-yLabelOffset)/2 + yAxisStringHeight/2;

        //pull down, which is basically the left offset, topOffset, then middle it by 
        //usin chart height and using text height.
        g2d.translate(translateDown, translateLeft);

        g2d.setFont(yFont);
        g2d.drawString(yAxisStr, leftOffset, topOffset);

        //reset
        g2d.setTransform(oldTransform);

        int xAxesLabelHeight = bottomOffset - xLabelOffset;

        //x label        
        g2d.setFont(xFont);
        g2d.drawString(xAxis, widthChart/2 + leftOffset - xAxisStringWidth/2, topOffset + heightChart + xLabelOffset + xAxesLabelHeight/2);

                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, // Anti-alias!
                RenderingHints.VALUE_ANTIALIAS_ON);
        //title
        g2d.setFont(titleFont);
        int titleX = (leftOffset + rightOffset + widthChart)/2 - titleStringWidth/2;
        int titleY = topOffset/2 + titleStringHeight/2;
        System.out.println("titleStringHeight " + titleStringHeight);
        System.out.println("titleX " + titleX);
        System.out.println("titleY " + titleY);
        System.out.println("topOffset " + topOffset);

        g2d.drawString(title, titleX, titleY);
    }
}

package com.bluewalrus;

import java.awt.Color;
/**
 * @copyright 2014 
 * @author Oliver Watkins (www.blue-walrus.com) 
 * 
 * All Rights Reserved
 */
public class Bar {

    double value; 
    Color color;
    String name;

    Bar(int value, Color color, String name) {
        this.value = value;
        this.color = color;
        this.name = name;
    }
}

package com.bluewalrus;
/**
 * @copyright 2014 
 * @author Oliver Watkins (www.blue-walrus.com) 
 * 
 * All Rights Reserved
 */
public class Axis {

    int primaryIncrements = 0; 
    int secondaryIncrements = 0;
    int tertiaryIncrements = 0;

    int maxValue = 100;
    int minValue = 0;

    String yLabel;

    Axis(String name) {
        this(100, 0, 50, 10, 5, name);
    }

    Axis(int primaryIncrements, int secondaryIncrements, int tertiaryIncrements, String name) {
        this(100, 0, primaryIncrements, secondaryIncrements, tertiaryIncrements, name);
    }

    Axis(Integer maxValue, Integer minValue, int primaryIncrements, int secondaryIncrements, int tertiaryIncrements, String name) {

        this.maxValue = maxValue; 
        this.minValue = minValue;
        this.yLabel = name;

        if (primaryIncrements != 0)
            this.primaryIncrements = primaryIncrements; 
        if (secondaryIncrements != 0)
            this.secondaryIncrements = secondaryIncrements;
        if (tertiaryIncrements != 0)
            this.tertiaryIncrements = tertiaryIncrements;
    }
}

Posted in Swing, Uncategorized | Tagged , , | Leave a comment

Maven Tutorial

..FOR dummys, duffers and the perpetually confused.

I have written this tutorial because I found Maven, like many others, frustratingly dry and obscure to understand. Nothing out there seemed to help me break through the nebulous aspects of Maven. For quite a while Maven was like this fog in which I flailed about in.

Coming from an ANT background, I saw ANT visually analogous to something like this :

maven1Notice the tangible structure?

On first inspection Maven is visually analogous to this :maven2Notice the lack of anything? It’s just a blob right?

Maven is a system for building software; a build automation tool that helps you ship out your software. If you have the vaguest understanding of Maven, you will also know that there is this thing called a POM file (pom.xml) which looks something like this :

<project>
  <groupId>com.mycompany.myapp</groupId>
  <artifactId>myapplication</artifactId>
  <version>2.0</version>
  <dependencies>
  </dependencies>
</project>

Maven attempts to treat projects in an object oriented fashion, and one of the central tenants in Maven is that a project is an object. This project only needs to be described once, in one XML file called the POM file.

The project has a primary key, and this is defined by its coordinates: groupId, artifactId, and version. Effectively its package root, a name, and a version number.

That is basically the only prerequisite. Then you can just build your project, through your IDE most typically, or by using the Maven command line:

mvn clean install

which executes the clean lifecycle, and all phases up to the install phase in the default lifecycle. This is most commonly considered “building” a project.

A look at dependencies

The easiest thing to understand about Maven is also the most beautiful thing about Maven. Rather than managing a number of libraries across a number of projects, everything is tied to this thing called a maven reactor, a type of super library that contains all your libraries that you are using. You can see it in an .m2 directory under your user profile usually (It doesn’t really have a sexy name because it is best to be hidden. ‘Out of sight out of mind’ as they say).

maven3

The above example is from when I was working on my multi level pie chart project. It’s not too hard to see what was going on here. My pom.xml file looked like

<groupId>com.bluewalrus</groupId>
<artifactId>multi_level_pie_chart</artifactId>
<version>1.0</version>
<packaging>jar</packaging>

As I was working on it I incremented the version number to 1.1, and after I did a build on the project, a new folder appeared called 1.1 with the file multi_level_pie_chart.jar in it.

The older versions are not deleted. There still hang around in the reactor. Who knows what other projects use these old versions?

Maven also allows you to point to the projects you need. As your own project is shipped to your .m2 directory, your own project is free to use whatever is in the .m2 directory. You just place in your dependency tags like so :

  <dependencies>
  ….
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
    </dependency>
  …
  </dependencies>

Maven will look for it in your .m2 directory. If it is not there then it will try and find the dependency on the web, and if it exists, download it to your .m2 directory. So in fact your dependency doesn’t have to exist in the .m2 directory. As long as you have an internet connection, it can exist as any Maven project in the internet.

As you work on multiple projects the maven reactor collates libraries that are needed as dependencies. On one project you may need JUnit1.4, but on another project you can only use JUnit1.3. The maven reactor neatly orders them based on version number.

maven4

If you hate configuration, Maven is for you.

Your project is its own source definition. There is no longer the hassle of faffing about with JAR files to make your project work, you no longer need to monkey around with pages and pages of configuration documents.

It’s all about the POM file. That’s what’s so cool about Maven!!

The configuration battles trying to get projects running across different IDEs, are no longer fought in the setups of IDEs. The battle front has been reduced to tinkering with the POM XML file.

Maven also allows for transitive dependencies. This dependency would only exists in the test phase:

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.0</version>
      <scope>test</scope>
</dependency>

A further five scopes are available, which describe a number of scenarios, such as having a dependency only at runtime, or that the dependency is provided by the application container.

A look at LifeCycles and Phases.

A lifecycle can contain many phases, and a phase contains many goals

maven5

Maven has 3 built in lifecycles.

Lifecycles contain a number of goals, grouped in phases. Maven has these lifecycles pre-configured for you to use, rather than offer an ad-hoc environment for you to chain them together as you please.

The clean :

Clean is what you would expect it to be. It blows away the target directory and that is about it. The reason that clean is not included in the default lifecycle is because a ‘clean’ is a time expensive activity. Once the target folder has been cleaned out, everything needs to be generated again (all the class files etc.). If clean were to be part of the default life cycle things would be slow because it would not be possible to build iterative changes. That is why the clean has been separated out of the default lifecycle.

The default :

The default life cycle is the main lifecycle that is used when building a project. I have enlargened the phases that you will encounter more often, and which are more important to understand.

maven6

Three of these phases should be familiar to most developers:

Compile – the software is compiled. Nothing too complicated here. Java code is converted to binary classes and put in the binary directory.

Test – All the unit tests are run. That’s all your JUnit tests that are sitting in your Test folder!

Package – after everything checks out as OK, the software needs to be bundled up ready for consumption. Ear file for JEE project, War file for Web project etc.

A couple of other commonly used phases

Validate – This checks all the directory structure is correction, that the conventions have been followed. Maven can’t work if people don’t follow the rules. The directory structure should look like this.

maven7

Install – (and his cousin deploy). These two are very similar. Install puts your packaged file, be it JAR, war, ear in the maven reactor (your .m2 directory). We have discussed this before. Doing an ‘install’ is usually considered doing a ‘build’

Deploy – takes this one step further, and moves your finished product to an internet location where it can be accessed from anywhere in the world.

[One word of warning, do not confuse the install and deploy phases with the actual deploying of your application. For example, these phases have nothing to do with the actual deploying of your web application to your server.]

Site :

Generates Documentation. We won’t cover this.

A look at Goals :

Goals describe actions. They are the atomic parts of Maven and are analogous to tasks in ANT. How they are accessed and manipulated is one of the hardest things to understand about Maven.

If you are familiar with ANT, ANT has tasks that are organized in a dependency tree. When one task executes, all the lower branches of the tree need to execute first. Well Maven is kind of similar, except that the tasks (the goals) are organized in a linear fashion.

So under the hood, Maven actually looks something like this :

maven8

It really is just a series of goals with no branching. Where ANT is tree-like, Maven is linear.

Plugins are a way that you can mess with this chain of goals, by attaching custom goals in certain phases.

A plugin, for example the wsimport plugin

    <build>
        <plugins>
            <plugin>
                <groupId>org.jvnet.jax-ws-commons</groupId>
                <artifactId>jaxws-maven-plugin</artifactId>
                <version>2.3</version>
                <executions>                    
                    <execution>
                        <id>wsimport-from-jdk</id>
                        <goals>
                            <goal>wsimport</goal>
                        </goals>
                        <configuration>
                            <executable>${tool.wsimport}</executable>
                            <wsdlUrls>
                                <wsdlUrl>http://WorkPC:8080/server-web/AirlineWS?wsdl</wsdlUrl>     
                            </wsdlUrls>
                            <packageName>com.bluewalrus</packageName>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

If you visit the website https://jax-ws-commons.java.net/jaxws-maven-plugin/ you will see that it binds to the “generate-sources” phase. If you run a clean install, then the goal of this plugin, wsimport, will execute along with along the other goals in the generate-source phase.

Goals in plugins can also be executed independently:

mvn [plugin]:[goal]

In the above wsimport example, you would execute :

mvn jaxws:wsimport

While it is not advisable to change the phase in which a plugin executes, the phase can be overridden by modifying the execution XML like so :

<execution>
  <phase>package</phase>
</execution>

So in summary, it is hard to understand what goals exist in Maven, and it is also hard to control where a goal executes in Maven. It is all under the hood stuff that only the plugins really know about. There really is little information about what goals exist in the default lifecycle.

A look at Project inheritance and project aggregation

All POMs inherit from the super pom. The super POM contains a lot of default configuration values. (eg. build.sourceDirectory = src/main/java) as well as other folder structures/properties that belong in the maven convention. You can look at the ‘effective pom’ which shows the merging of the pom files.

inheritance

It is also possible to make your projects inherit from other projects that you have created. Common configuration can be refactored out into the parent making maintenance much easier.

    
<project>
  <parent>
    <groupId>com.mycompany.app</groupId>
    <artifactId>my-app</artifactId>
    <version>1</version>
    <relativePath>../parent/pom.xml</relativePath>
  </parent>
  <modelVersion>4.0.0</modelVersion>
  <artifactId>my-module</artifactId>
</project>

aggregation

Projects can also reference sub projects. This is called project aggregation.
This example shows how you might aggregate a number of child projects (called modules) in the parent project. When the parent project is built, all the modules will be built.

  
<project>
  <groupId>com.bluewalrus</groupId>
  <artifactId>myWebApp</artifactId>
  <packaging>pom</packaging>
  <version>1.0-SNAPSHOT</version>
    <modules>
        <module>myWebApp-ear</module>
        <module>myWebApp-ejb</module>
        <module>myWebApp-web</module>
    </modules>
</project>

This example is a common pattern in JEE. It is a web application which is a packaging of a WAR and an EJB (as JAR) file, into an EAR file.

Note: you still need to define dependencies between projects, if order is important. For example, in the above, the WAR and JAR modules need to build before the EAR. In the EAR project I would expect to see something like this in the POM-file:

    
    <dependencies>
        <dependency>
            <groupId>com.bw</groupId>
            <artifactId>myWebApp-ejb</artifactId>
            <version>1.0-SNAPSHOT</version>
            <type>ejb</type>
        </dependency>
        <dependency>
            <groupId>com.bw</groupId>
            <artifactId>myWebApp-web</artifactId>
            <version>1.0-SNAPSHOT</version>
            <type>war</type>
        </dependency>
    </dependencies>

Anyway, I hope this tutorial was useful to some. It hasn’t covered everything, but I have tried to present the core concepts that people need to understand.


Posted in Uncategorized | Tagged , , | Leave a comment

Multi Level Pie Chart in Java

ml1_1

Update !

This pie chart compoment has now been moved into the Iceberg Charts project . Please use Iceberg Charts for a more complete charting experience.

Pie charts are pretty boring. I hadn’t found anything out there which resembled a multi level pie chart. A multi level pie chart, is basically a pie chart with a number of levels. Each level is in effect an expansion of the slice below it. A multi level pie chart basically represents a tree structure, where each node has a magnitude.

The component I have written requires a tree structure of Segment objects. A typical real world scenario where you would use a multi level pie chart would be to view the disk usage on a computer. A simplistic example I created is shown below.

ml2_1

The code I needed to generate the above chart is just this :

      
ArrayList values = new ArrayList();
        values.add(new Segment(0, null, 15, "music", Color.RED));
        values.add(new Segment(0, null, 52, "photos", Color.BLUE));
        values.add(new Segment(0, null, 33, "applications", Color.GREEN));

        Color c = Color.BLUE;
        Segment s0 = values.get(0);
        Segment s1 = values.get(1);
        Segment s2 = values.get(2);

        {
            c = s0.color;
            s0.children.add(new Segment(1, s0, 80.0, "jazz", darken(c)));
            s0.children.add(new Segment(1, s0, 7.0, "rock", darken(darken(c))));
            s0.children.add(new Segment(1, s0, 13.0, "classical", darken(darken(darken(c)))));
        }
        {
            c = Color.cyan;
            s1.children.add(new Segment(1, s1, 80.0, "holiday snaps", darken(c)));
            s1.children.add(new Segment(1, s1, 7.0, "weddings", darken(darken(c))));
            s1.children.add(new Segment(1, s1, 13.0, "baby", darken(darken(darken(c)))));
        }
        {
            c = Color.YELLOW;
            s2.children.add(new Segment(1, s2, 80.0, "Windows", darken(c)));
            s2.children.add(new Segment(1, s2, 7.0, "Favourites", darken(darken(c))));
            s2.children.add(new Segment(1, s2, 13.0, "Database", darken(darken(darken(c)))));
        }
        Segment s20 = s2.children.get(0);
        {
            c = Color.PINK;
            s20.children.add(new Segment(2, s20, 50.0, "Office", darken(c)));
            s20.children.add(new Segment(2, s20, 7.0, "Visio", darken(darken(c))));
            s20.children.add(new Segment(2, s20, 43.0, "Paint", darken(darken(darken(c)))));
        }
        Segment s22 = s2.children.get(2);
        {
            c = Color.WHITE;
            s22.children.add(new Segment(2, s22, 50.0, "Oracle", darken(c)));
            s22.children.add(new Segment(2, s22, 50.0, "Access", darken(darken(c))));
        }

        Segment s200 = s20.children.get(0);
        {
            c = Color.ORANGE;
            s200.children.add(new Segment(3, s200, 10.0, "Word", darken(c)));
            s200.children.add(new Segment(3, s200, 80.0, "Excel", darken(darken(c))));
            s200.children.add(new Segment(3, s200, 5.0, "Access", darken(darken(darken(c)))));
            s200.children.add(new Segment(3, s200, 5.0, "Powerpoint", darken(darken(darken(darken(c))))));
        }

        Segment s10 = s1.children.get(0);
        {
            c = Color.white;
            s10.children.add(new Segment(2, s10, 30.0, "Rome", darken(c)));
            s10.children.add(new Segment(2, s10, 20.0, "Paris", darken(darken(c))));
            s10.children.add(new Segment(2, s10, 10.0, "London", darken(darken(darken(c)))));
            s10.children.add(new Segment(2, s10, 10.0, "Beach", darken(darken(darken(darken(c))))));
            s10.children.add(new Segment(2, s10, 30.0, "China", darken(darken(darken(darken(c))))));
        }

PieChart pieChart = new PieChart(values);

In order for it to not look like a multi colored mess, I have applied a gradient-like effect, by darkening the colors sequentially after the initial color is set for a segment. My darken method is simply :

 

    private Color darken(Color c) {
        return c.darker();
    }

I think this is quite effective, but there may be other ways or algorithms to implement a set of related colors. You could experiment with incrementally adjusting the hue, saturation or a rgb value.

A bit about the design of this component :

For a simple pie chart there are really only three things to consider for a pie slice. They are

  1. the starting angle (calculated from what slices have previously been drawn),
  2. the magnitude (if it is a percent it needs to be converted to an angle)
  3. and of course the color.

ml3

This is simple enough to implement. However for a multi level pie slice there are many other things to consider :

  • Relative start angle.The start angle needs to be converted, because this pie slice may not be the first level, and therefore a scaling would need to be applied.
  • Parent. The slice will have a parent, if it is not on the first level.
  • Relative magnitude. The angle, or percent, needs to be scaled based on what its parent is. If the parent represents 50% and the parents parent is on the first level and represents 50%, then the scalings would have to be combined to 25%. This 25% would then have to be applied to the original magnitude.

 

ml4

Also to consider would be

  • Circle1 – a circle that represents the outer rim of the slice.
  • Circle2 – a circle that would need to be subtracted from the arch created from circle1.

These circles would need to be calculated depending on a number of factors. The extend and starting point would of course need to be taken into account, and the number of circles needed would also depend on the total depth of the tree structure.

Given all these things to consider, the segment is a special type of object. Omitting the getters and setters for clarity, here is the class I came up with for a segment. A lot of the values need to be calculated.

 
public class Segment {

    double startAngle;//starting angle (calculated)
    double angle; // from magnitude (calculated)

    Color color; //color (supplied by user)
    int level; //level 0 = first level pie chart (supplied by user)
    Segment parent; //parent segment (supplied by user)
    Double magnitude; //percent (supplied by user)
    String name; //name of segment (supplied by user)

    Area area; //geometric shaped (calculated)
    Point midpoint; //a calculated midpoint that determines where the text should be drawn (calculated)
}

(jar file is here)

If you would like to use this component in a project, or would like to look at the source code, please contact me 


Posted in Uncategorized | Tagged , , , | Leave a comment

Swing and Web Services Tutorial

jet_jumbo

Swing and web services have been around for many years, however it has only been in recent times that things have gotten much easier in implementing web services and building complex Swing applications. This is mainly due to improvements in the Java language and better IDEs.

This tutorial aims to be a comprehensive guide to getting an end-to-end configuration of a Swing application communicating with a web service deployed on the server.

This tutorial also touches on Maven and the Netbeans IDE, two technologies that go hand in hand. You don’t specifically need either of these technologies to get up and running, but for the purposes of this tutorial it is highly recommended.

Part 1. Client

We are going to start off with a basic application. In our example we are going to be building an airline application that talks to a web service. Its very simple, just a table which lists all the airlines, and a button which creates a new row in the table.

Create Project

Lets create a Maven project and call it ‘client’. It is going to be our desktop client, so we want it to be packaged as a jar file.

client_server1

Write Some Swing Code

We want a simple entry point to our application, so we will add a main method to our MainFrame class which serves as the main Window for the application.

public class MainFrame extends JFrame {

    public MainFrame() {

        JPanel panel1 = new JPanel();

        AirlinePanel userPanel = new AirlinePanel();

        panel1.setLayout(new BorderLayout());
        panel1.add(userPanel);

        this.getContentPane().add(userPanel);

        this.setTitle("Airlines");
        this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        this.setLocationRelativeTo(null);

        pack();
        setSize(900, 600);
        setVisible(true);
    }

    public static void main(String[] args) {
        new MainFrame();
    }
}

Inside out frame code you may notice an AirlinePanel class, which is basically a JPanel with a JTable and buttons. Let’s add our table inside a scrollpane, and add a button to the top. The aim of the table is to list all the airlines in the system, and the button will eventually create new rows in the table after persisting across the network.


public class AirlinePanel extends JPanel {

    private JScrollPane scrollPane;
    private JTable userTable;

    public AirlinePanel() {
        initComponents();
    }

    private void initComponents() {

        this.setLayout(new BorderLayout());

        scrollPane = new JScrollPane();
        userTable = new JTable();
        AirlineTableModel model = new AirlineTableModel();

        userTable.setModel(model);
        scrollPane.setViewportView(userTable);

        JPanel buttonPanel = new JPanel();

        JButton createUserButton = new JButton("Create Airline");

        buttonPanel.add(createUserButton);

        this.add(scrollPane);
        this.add(buttonPanel, BorderLayout.NORTH);
    }
}

Swing uses the MVC pattern for its complex components, so for our table object, we need to have a model object that contains the business data. Lets create AirlineTableModel, an AbstractTableModel, which acts as a model for AirlineVO objects.


public class AirlineTableModel extends AbstractTableModel {

    private List airlines = new ArrayList();

    @Override
    public String getColumnName(int i) {
        switch (i) {
            case 0:
                return "airline name";
            case 1:
                return "number of fleet";
            case 2:
                return "home city";
            case 3:
                return "country";
        }
        return "";
    }        
    public int getRowCount() {
        return this.airlines.size();
    }
    public int getColumnCount() {
        return 4;
    }
    public Object getValueAt(int rowIndex, int columnIndex) {
        AirlineVO a = this.airlines.get(rowIndex);

        switch (columnIndex) {
            case 0:
                return a.getName();
            case 1:
                return a.getNumberOfFleet();
            case 2:
                return a.getHomeCity();
            case 3:
                return a.getCountry();
        }
        return null;
    }
    public void setAirlines(List airlines) {
        this.airlines = airlines;
    }
}

So now we should have three classes :

  • MainFrame
  • AirlinePanel
  • AirlineTableModel

However AirlineVO is missing.

Create an AirlineVO class somewhere in the project. We will be deleting this local AirlineVO class a bit later, and instead we will be using the web service generated AilineVO object. But for now you can add some dummy getter and setters into the code just so that the client application code works.

Now let’s move on to the web service project.

Part 2. Webservices

Create Web Service Project

Create a web application project. In Netbeans you can do this via choosing a Web Application project.

client_server2

The Pom project you have should be named like this. We want the groupId to be the same for the client and the server. It needs to be packaged as a WAR file, and dependencies should be present which includes javaee-web-api and webservices-rt. Both these scopes should be set to PROVIDED. This basically means the web server has its own versions of these two libraries which it will use instead.

client_server3

Create the Web Service Class

Create a java class called AirlineWS. This will be our entry point for our airline application. All methods that we need will exist in this class.

On Netbeans go to New —> WebService

Annotate the class with the @WebService annotation, which tell the application server to treat the class as a web service.

@WebService(serviceName = "AirlineWS")
public class AirlineWS {

        /**
     * This is a sample web service operation
     */
    @WebMethod(operationName = "hello")
    public String hello(@WebParam(name = "name") String txt) {
        return "Hello " + txt + " !";
    }
}

Create the Value Object Class

In our client code we can see that the code uses something called an AirlineVO object. We want to define this on the server side as well. If we also have the AirlineVO defined in the web service (WSDL), then there is no need to have a separately maintained copy on the client side. The AirlineVO object can be generated via a ws-import (more on that later).

NOTE : While in this example we are using the AirlineVO object throughout the client code, this in general is bad practise. Once the VO object is received by the client, it should be converted to a client specific object: eg: Airline guiAirlineObject = new Airline(airlineVO). This reduced the dependencies on the generated code, and also allows for decorations. That is to say, it is a decorator pattern.

So we create an AirlineVO class and put it in the web server project.

public class AirlineVO {

    public AirlineVO() {}

    public AirlineVO(String name, Integer numberOfFleet, String homeCity, String country) {
        this.name = name;
        this.numberOfFleet = numberOfFleet;
        this.homeCity = homeCity;
        this.country = country;
    }

    private String name;
    private Integer numberOfFleet; 
    private String homeCity;
    private String country;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getNumberOfFleet() {
        return numberOfFleet;
    }

    public void setNumberOfFleet(Integer numberOfFleet) {
        this.numberOfFleet = numberOfFleet;
    }

    public String getHomeCity() {
        return homeCity;
    }

    public void setHomeCity(String homeCity) {
        this.homeCity = homeCity;
    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }
}

Implement the WebService Methods

We can now add some business logic to the web server. Normally a web server delegates to another layer such as the EJB layer, however for demonstration purposes this web service will be our end point. In a constructor we will create some of these Value objects and put them into an array list.

We will then create a couple of methods. We need to annotate them, this way the web server know to treat them as a web service end point.

@WebService(serviceName = "AirlineWS")
public class AirlineWS {

    ArrayList airlines = new ArrayList();

    public AirlineWS() {

        airlines.add(new AirlineVO("QANTAS",2, "Australia","Sydney"));
        airlines.add(new AirlineVO("Lufthansa",1000, "Germany","Frankfurt"));
    }

    @WebMethod(operationName = "create")
    public void create(@WebParam(name = "airline") AirlineVO airlineVO) throws Exception {
        airlines.add(airlineVO);
    }

    @WebMethod(operationName = "getAll")
    public ArrayList getAll() throws Exception {
        return airlines;
    }
}

We can add a constructor which creates some dummy data. Of course we should have a proper database for our application, but for illustrative purposes this should suffice. The other two methods do a basic add, and a basic create.

Clean, Build, and Deploy

Now that we have our server code we can clean and build the project. In Netbeans there is a Clean and Build option on the context menu for the project.

client_server4

Follow this up with Run. This can also be found on the project context menu.

‘Running’ a server project in Netbeans and in most other IDEs, actually deploys the application to the server. Per default Netbeans has a Glassfish instance running on localhost, so an instance of Glassfish is booted up, if it hasn’t already been started, and the WAR file is deployed as an application.

If you visit the administration console for glassfish, you will see something like this.

client_server5

From this screen you can select ‘View Endpoint’ and view that the WSDL is there. This is what is exposed to the outside world, and is what ws-import uses to figure out what classes are needed to talk to this interface.

Part 3. Back to the Client…

Delegate

Let’s create the skeleton of a delegate class as we want to use the delegate pattern to shield the GUI code from all the backend code. Swing is also not Thread-safe so we also need to make all calls to the backend through a thread safe singleton.

public class Delegate {

    private Delegate() {
    }

    public static Delegate getInstance() {
        return DelegateHolder.INSTANCE;
    }

    private static class DelegateHolder {
        private static final Delegate INSTANCE = new Delegate();
    }
}

WsImport

Now we need to add wsimport support. WsImport is a maven plugin that needs to be defined in the POM file, in the plugins section. The webservice endpoints that we are interested in should be defined in the <wsdlUrl> tag. In our case, it is the AirlineWS web service.

client_server6

Clean and build the client project. The wsimport plugin attaches itself to the build process, and you may notice that building has gotten a little slower. After it has finished you may now notice a folder called ‘Generated Sources (wsimport)’.

client_server7

It contains all the artefacts that the wsimport has created during the build process. We can get access to a port for a particular web service, and from that port object we can call any method we want. This is how we use the generated code to do this:

com.bluewalrus.AirlineWS_Service service = new com.bluewalrus.AirlineWS_Service();
com.bluewalrus.AirlineWS port = service.getAirlineWSPort();
port.create(airline);

Notice how the AirlineVO class is also there. This is not the same object as exists on the server side. This object was generated based on the Web Service definition (WSDL).

Lets add two methods to the delegate object, using the generated code :

public class Delegate {

    private Delegate() {
    }

    public static Delegate getInstance() {
        return DelegateHolder.INSTANCE;
    }

    private static class DelegateHolder {
        private static final Delegate INSTANCE = new Delegate();
    }

    public void createAirline(AirlineVO airline) {

        try {

            com.bluewalrus.AirlineWS_Service service = new com.bluewalrus.AirlineWS_Service();
            com.bluewalrus.AirlineWS port = service.getAirlineWSPort();

            port.create(airline);

        }catch(Exception e) {

            JOptionPane pane = new JOptionPane(e.getMessage(), JOptionPane.ERROR_MESSAGE);
            pane.setVisible(true);
        }
    }

    public List getAirlines() {

        com.bluewalrus.AirlineWS_Service service = new com.bluewalrus.AirlineWS_Service();
        com.bluewalrus.AirlineWS port = service.getAirlineWSPort();

        try {
            return port.getAll();
        } catch (Exception e) {
            JOptionPane pane = new JOptionPane(e.getMessage(), JOptionPane.ERROR_MESSAGE);
            pane.setVisible(true);
        }
        return null;
    }

}

Now we can call these delegate methods from within the Swing code.

We can do two things.

  1. On the create button add an actionlistener. In a later tutorial we will add a proper dialog for creating a new airline object. For now, we just send the same data across to the server, as in this tutorial we are just interested in a proof of concept.
  2. At the end of the initComponents method we call getAll from the web service. This will getall the value object from the backend and display them in the table. We also call a getAll in the create button code, because we want to perform a refresh of the data once we have created something.
public class AirlinePanel extends JPanel {

    private JScrollPane scrollPane;
    private JTable userTable;

    public AirlinePanel() {
        initComponents();
    }

    private void initComponents() {

        this.setLayout(new BorderLayout());

        scrollPane = new JScrollPane();
        userTable = new JTable();
        AirlineTableModel model = new AirlineTableModel();

        userTable.setModel(model);
        scrollPane.setViewportView(userTable);

        JPanel buttonPanel = new JPanel();

        JButton createUserButton = new JButton("Create Airline");

        createUserButton.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {

                AirlineVO vo = new AirlineVO();
                vo.setCountry("Egypt");
                vo.setName("Egypt Air");
                vo.setHomeCity("Cairo");
                vo.setNumberOfFleet(150);

                Delegate.getInstance().createAirline(vo);

                List airlines = Delegate.getInstance().getAirlines();

                AirlineTableModel model = (AirlineTableModel)userTable.getModel();

                model.setAirlines(airlines);

                model.fireTableDataChanged();

            }
        });

        buttonPanel.add(createUserButton);
        List airlines = Delegate.getInstance().getAirlines();

        model.setAirlines(airlines);
        model.fireTableDataChanged();

        this.add(scrollPane);
        this.add(buttonPanel, BorderLayout.NORTH);
    }
}

You should see something like this. The table is being filled from data supplied by the web service, and clicking on the Create button creates a new row in the table.

client_server8

(Source Code is here)

Comments are very appreciated J


Posted in Swing | Tagged , , , , | Leave a comment

Setting up an Oracle Database in Glassfish for JEE Applications

Setting up an Oracle database connection in Glassfish 4 is relatively simple, but can be confusing and a little frustrating at times. Hopefully this mini tutorial will get you up and running quickly and painlessly.

Setup Pool

Select Resources –> JDBC –> JDBC Connection Pools and then select New.

For Step 1., enter in a name (in this example jdbc). Select Resource Type to be of type javax.sql.DataSource, and select Oracle as Database driver vendor :

dbglass1

Leave everything as is in Step 2.

Look at your database settings in your DB explorer. This example shows how my database looks like in Oracle SQL Developer

dbglass3

You need to get values from this properties screen and create a properties string that glassfish server can understand.

Click on the pool you created previously and leave everything as is in General and in Advanced tabs. However in the Additional Properties tab add three strings. The username and password (username would be SYS from the previous screenshot).

The URL is formed by adding jdbc:oracle:thin at the start. Looking at the previous screenshot, this would be followed by the username again (SYS), the characters @//, the server hostname (mydbserver), the port number (1522) and the service name (DB1).

dbglass2

Setup Resource

Save these connection pool properties. Now go to the JDBC-Resource node and select New. Select the pool in the drop-down list (In this case it would be jdbc). Give the resource a name (which is also its JNDI name). In this example we will call it myJDBC.

dbglass4

 

Configure Persistence XML

Finally we just need to reference this resource in our persistence XML file. The name we gave the resource (myJDBC) needs to be set in the XML file like so :

dbglass5

 

Now you should be connected. Write some EJBs and see if they talk to the database.

 


Posted in Uncategorized | Tagged , , , | Leave a comment

Simple Piechart in HTML5 and Javascript

piejs

I created a little pie-chart component in Javascript for anyone who wants to spice up the HTML5 webs-site.

Basically the requirements are that you define the slices like this:

    
            var slices = {
                "percentages": [25,20,20,5,30],
                "colors": ['gray', 'green', 'blue', 'pink', 'orange'],
            }

            var piechart  = Object.create(PieChart);
            piechart.segmentMode = false;
            piechart.drawPieChart(slices, 'piechart1');

The slices object contains two arrays in it. The first array is a list of the percentages, and the second array contains the corresponding colors. Create the pie chart as a variable via Object.create, and call drawPieChart(..) method passing in the slices object, and a string which is the id of a canvas on your page.

There are two modes.

  • Standard Pie Chart – a basic pie chart with black outline.
  • Segment Pie Chart – the center is hollowed out giving it a slick looking feel. To use this mode just write piechart.segmentMode = true.

Here is the Piechart component code :

       
       var PieChart = {

                drawPieChart: function (slices, id) {

                    var percentElements = slices.percentages;
                    var colorElements = slices.colors;

                    var canvas = document.getElementById(id);
                    var context = canvas.getContext("2d");

                    var centerX = canvas.width / 2;
                    var centerY = canvas.height / 2;
                    var radius = 80;

                    context.beginPath();
                    var endAngle = 2 * Math.PI

                    var lastAngle = 0;

                    for (var i = 0; i < percentElements.length; i++) {
                        var percent = percentElements[i];
                        var color = colorElements[i];

                        var currentSegment = endAngle * (percent/100);
                        var currentAngle = currentSegment + lastAngle;

                        context.beginPath();
                        context.moveTo(centerX, centerY)
                        context.arc(centerX, centerY, radius, lastAngle, currentAngle, false);
                        context.closePath();

                        lastAngle = lastAngle + currentSegment;

                        context.fillStyle = color;
                        context.fill();

                        if (this.segmentMode) {
                            context.lineWidth = 2;
                            context.strokeStyle = 'white';

                        }else {
                            context.lineWidth = 1;
                            context.strokeStyle = 'black';

                        }
                        context.stroke();
                        context.fill();
                    }
                    if (this.segmentMode) {

                        context.beginPath();
                        context.fillStyle = 'white';
                        context.arc(centerX, centerY, radius - 20, 0, 2 * Math.PI, false);
                        context.fill();
                    }
                }
            }

There is some basic maths involved in creating the pie chart. The most odd thing is that in HTML5 you do not define the angles from 360 degrees, but as a percent of 2 * PIE.

There is no legend in this component, but it would be pretty easy to create. Given a table element with id legend, we can write some code like this to show the colors in a table.

          
var slices = {
                "percentages": [25,20,20,5,30],
                "colors": ['gray', 'green', 'blue', 'pink', 'orange'],
                "elements": ['banana', 'clock', 'beetroot', 'planet', 'ball']
            }

            var legend = document.getElementById('legend');

            for (var i = 0; i < slices.percentages.length; i++) {

                var tr = document.createElement('tr');

                legend.appendChild(tr);

                var p = slices.elements[i];
                var c = slices.colors[i];

                var td1 = document.createElement('td');
                var td2 = document.createElement('td');

                tr.appendChild(td1);
                tr.appendChild(td2);

                td1.innerHTML = slices.elements[i];
                td2.style.background = slices.colors[i];
                td2.style.width = '40px';
            }

The complete code for the image at the top of the page is shown below. You may use it any way you wish. Just remember it will only work in a modern browser with HTML5 support suh as the latest browser from Chrome or Firefox.

<head>
    <script>
        window.onload = function() {

            var PieChart = {

                drawPieChart: function (slices, id) {

                    var percentElements = slices.percentages;
                    var colorElements = slices.colors;

                    var canvas = document.getElementById(id);
                    var context = canvas.getContext("2d");

                    var centerX = canvas.width / 2;
                    var centerY = canvas.height / 2;
                    var radius = 80;

                    context.beginPath();
                    var endAngle = 2 * Math.PI

                    var lastAngle = 0;

                    for (var i = 0; i < percentElements.length; i++) {
                        var percent = percentElements[i];
                        var color = colorElements[i];

                        var currentSegment = endAngle * (percent/100);
                        var currentAngle = currentSegment + lastAngle;

                        context.beginPath();
                        context.moveTo(centerX, centerY)
                        context.arc(centerX, centerY, radius, lastAngle, currentAngle, false);
                        context.closePath();

                        lastAngle = lastAngle + currentSegment;

                        context.fillStyle = color;
                        context.fill();

                        if (this.segmentMode) {
                            context.lineWidth = 2;
                            context.strokeStyle = 'white';

                        }else {
                            context.lineWidth = 1;
                            context.strokeStyle = 'black';

                        }
                        context.stroke();
                        context.fill();
                    }
                    if (this.segmentMode) {

                        context.beginPath();
                        context.fillStyle = 'white';
                        context.arc(centerX, centerY, radius - 20, 0, 2 * Math.PI, false);
                        context.fill();
                    }
                }
            }

            var slices = {
                "percentages": [25,20,20,5,30],
                "colors": ['gray', 'green', 'blue', 'pink', 'orange'],
                "elements": ['banana', 'clock', 'beetroot', 'planet', 'ball']
            }

            var piechart  = Object.create(PieChart);
            piechart.segmentMode = false;
            piechart.drawPieChart(slices, 'piechart1');

            var slices = {
                "percentages": [25,20,20,5,30],
                "colors": ['gray', 'green', 'blue', 'pink', 'orange'],
                "elements": ['banana', 'clock', 'beetroot', 'planet', 'ball']
            }

            var piechart  = Object.create(PieChart);
            piechart.segmentMode = true;
            piechart.drawPieChart(slices, 'piechart2');

            var legend = document.getElementById('legend');

            for (var i = 0; i < slices.percentages.length; i++) {

                var tr = document.createElement('tr');

                legend.appendChild(tr);

                var p = slices.elements[i];
                var c = slices.colors[i];

                var td1 = document.createElement('td');
                var td2 = document.createElement('td');

                tr.appendChild(td1);
                tr.appendChild(td2);

                td1.innerHTML = slices.elements[i];
                td2.style.background = slices.colors[i];
                td2.style.width = '40px';
            }
        }
    </script>
</head>
    <body>
        <canvas id='piechart1' width='200px' height='200px'></canvas>
        <canvas id='piechart2' width='200px' height='200px'></canvas>
        <table id='legend'></table>
    </body>
</html>

Enjoy! Please leave a Comment!


Posted in JavaScript | Tagged , , | 3 Comments

Javascript IDE Review

…a look at refactoring, intellisense, navigability, and usability.

javascriptIDEs

As agile software development becomes more commonplace, it becomes more and more essential that the tools we use allow us to develop software in a flexible fashion. Particularly important is refactoring. Being able to refactor is what allows is to respond to change. It makes our code evolve overtime. For example renaming a method, or changing a methods parameters should not be a programming disaster, it should be effortless and pain free, allowing our software to develop organically.

For the last year I have been using Javascript for a project. After 10 years of Java it has been a pretty hellish first few months, but things have improved once I learned how much a good IDE can leverage the pain away. Javascript’s dynamic nature will never allow for smooth refactoring like Java in Eclipse, but you need at least SOME intellisense, code navigability and refactoring support if you are going to be do anything other than ‘script kiddy’ stuff.

Having an IDE that understands the language you are developing in allows project wide changes, as it can offer a layer of control like the Eclipse platform does for Java developement. A Javascript IDE should understand Javascript. Ideally a Javascript IDE should also understand JSDoc which can also offer type checking. You really can’t afford this notion of ‘duck typing’ when you have a multi library project with 20,000+ lines of Javascript code.

I set up a list of criteria below, and ran these against four Javascript IDEs that I would consider the ‘main players’ in this realm.

Object Rename  If I rename the method methodXYZ in the method test in the below example, it should not affect the method in ObjectA.

function test() {
  var v = new ObjectB();
  v.methodXYZ();
  }

function ObjectA() {
  this.methodXYZ = function() {
  };
}

function ObjectB() {
  this.methodXYZ = function() {
  };
}
Javascript File Rename If I rename a javascript file, then I want the rename to also be carried through to my HTML index file. Ie. The IDE has to know of the relationship between the index.html file and the javascript files in the project.
Rename var or param Can I rename local variable or parameters in the scope of a method?
Intelligent Intellisense Entering a ‘dot’ will bring up a pop up list of methods.If I type ‘.’ After declaring ‘v’ in the example below :

function ObjectB() {
  this.methodB = function() {
  };
}

function ObjectA() {
  this.methodA = function() {
  var v = new ObjectB();
  v.
  };
}

Then I want my IDE to intelligently know that methodB is an available method of v because v is an ObjectB object.

Create Method Hint If I type in a method that does not yet exist, I want a graphical hint or shortcut to allow the quick creation of a method stub (offering perhaps various options for scope).

function test() {
  var v = new ObjectA();
  v.methodXYZ();
  v.createMe(); //does not yet exist
}

function ObjectA() {
  this.methodXYZ = function() {
  };
}
Unreachable Code Example

function test() {
  var v = new ObjectB();
  throw 'an error';
  v.methodXYZ();
}

I want my IDE to highlight or underline unsed code in some way to make it stand out.

jQuery intellisense Whenever I press ‘.’ In this scenario :

$(myElement).

I want to have a list of all JQuery methods.

Code Navigation
Method Step Into I want my IDE to know how to navigate to the methodXYZ in ObjectA, through the use of a function key or short cut key.

function test() {
  var v = new ObjectA();
  v.methodXYZ();
  }

function ObjectA() {
  this.methodXYZ = function(){
  };
}

function ObjectB() {
  this.methodXYZ = function() {
  };
}

This should also work across javascript files.

Var Step Into In the case of a simple locally scoped variable When doing the ‘step into’, I want my cursor to move to the line of the ‘var’ assignment.In the case of a variable defined with this. The Step Into should be as intelligent as possible, and try to determine where it first has been defined/instantiated.Ideally all instances of the variable should be clearly highlighted.
Find Usages I want my IDE to know how to find usages of an object or method across the whole project.In Eclipse CTRL-SHIFT-G is used to fine Java methods. A list is brought up showing where this object is being used across the project or projects. Something similar should also be possible in Javascript. Although Javascripts dynamic nature prevents this from being perfect, some basic string matching across the whole project should be at least offered.
JSDoc Does the IDE support JSDoc to some degree?
JSDoc Type Checking Is it possible to do Type Checking by using JSDoc or perhaps another means? Can the IDE perform some basic error checking before sending the code to runtime?For example in the below example the call of methodXYZ in test is wrong because it is expecting an object of ‘type’ ObjectA through JSDoc markup. Can the IDE visually underline or highlight these errors in code?

function test() {
  var v = new ObjectB();
  v.methodXYZ('some string');
}

function ObjectB() {

  /**
  * @param {ObjectA} param
  */
  this.methodXYZ = function(param) {
  };
}

function ObjectA() {
  this.methodXYZ = function() {
  };
}
Method Encapsulation and JSDoc Using the JSDoc @private annotation, is it possible to hide methods?Does intellisense then hide the method? Or at least indicate that it should treated as private?

These are the results :

Webstorm

Aptana 3.4.2

Netbeans

Visual Studio

Object Rename Yes. But sometimes it doesn’t understand the context and does a string replace. No Yes.Weirdly does not allow a rename from the menu. No
Javascript File Rename Yes. No Yes. No
Rename var or param Yes. No. Seems to lack any refactoring capabilities. yes No
Intelligent Intellisense Yes No. Only shows methods from javascript Function and Object object Yes Yes.
Create Method Hint Webstorm :Creates in method or globally in file. Cant figure out type. No No. Offers ‘Surround with function’ which is almost as good. No
Unreachable Code Yes. Underlines in gray with tooltip No No. No.
Unused variables yes No Yes no
jQuery intellisense yes No Yes No
Method Step Into Yes.Across files? = yes No. Some code navigation, but extremely limited. Yes.Across files? = yes YesAcross files? = No
Var Step Into Yes No yes yes
Find Usages Yes No No No. Not apparent to me that it can.
Pull Out local to global variable Webstorm: doesn’t seem to work No No No
JSDoc Yes No Yes No
JSDoc Type Checking Yes. Reasonable support No Yes No.
Method Encapsulation and JSDoc Yes. No No. No.

Conclusion

I have been developing in Webstorm for the past 8 months or so, and I thought the refactoring abilities, intellisense and code navigation were tools that are standard for serious Javascript developement environment. Without these refactoring tools, developing complex Javascript applications has been like stumbling around in the dark, a trip back to the ‘stone age’ of software development where everything was developed in text editors. Understanding how a small piece of code works could cost days.

I assumed that Javascript IDEs would have this kind of stuff per default, but I have been pretty surprised at how much lacking the software is out there. Javascript is so ubiquitous on the web, so its something all web IDEs should have.

Webstorm comes out way ahead in my research. Jetbrains, a pioneer in Java refactoring have tried as hard as possible to squeeze out as much refactoring as is possible in a dynamic language such as Javascript. Some things aren’t perfect, refactorings have sometimes just behaved as a text ‘search’ and ‘replace’, and have some nasty side effects at times, but a preview screen offers pre-emptive screening, and renaming methods has been overall quite pleasing. (TIP : Make you Javascript methods unique to avoid conflicts!). Using JSDoc in Webstorm has been great too.

Netbeans is also pretty good, however it lacks some basic features. Why can’t I ‘find usages’ for a method, even if it is just a string search? It would help immensely. You can’t delete or change code if you don’t know where its being used. Renaming a method doesn’t seem to register across files which is a shame too.

Visual Studio is fairly poor with its Javascript support and on top of that it feels like driving a ten ton truck through a Japanese rock garden. Aptana offers almost nothing, and is really just a glorified text editor. Maybe it offers great PHP/Ruby development? In any case you can throw it in the same basket as Spket, Sublime.

So Webstorm comes out on top. Even though I have been using it, i was quite surprised that there isn’t anything out there to compete with it. Hopefully that will change.

 

 


Posted in JavaScript | Tagged , , , , , , | 6 Comments

Swing and Design Patterns – Part 4. Mediator Pattern

Assume we have a component hierarchy that looks like the following diagram. Some component on one branch (say a button), wants to update something on another branch (say text in a text field).


med

This scenario occurs very often in complex GUIs. Some examples may be :

  • Blinking of a mail icon when mail is recieved.
  • Update a table cell of a trade, after closing it in a dialog.
  • Any inner-framed application that requires inter frame communication (like photoshop)

To solve this communication dillema we can ‘bubble’ through a reference to the parent frame. In the constructor of the container components we pass this reference through.

public class Mediator extends JFrame{
    public Mediator() {

        MyPanel panel = new MyPanel(this);

        this.getContentPane().add(panel);

        this.setVisible(true);

        this.setBounds(0, 0, 1024, 708);

        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

We thread the parent frame through the constructor of our initial panel.

We want this parent frame to permeate all our components so we keep passing it through the constructors of embedded panels :

   
class MyPanel extends JPanel {

        private final JFrame parent;

        private final AnotherPanel anotherPanel;

        public MyPanel(JFrame parent) {

            this.parent = parent;

            anotherPanel = new AnotherPanel(this.parent);

            this.add(anotherPanel);
        }

        public AnotherPanel getAnotherPanel(){
            return anotherPanel;
        }
    }

…and so on. Lets assume the class AnotherPanel calls a Dialog. The parent frame object also needs to be passed through the dialog too :

    
class AnotherPanel extends JPanel {

        private final MyDialog dialog;

        public AnotherPanel(JFrame parent) {

           dialog = new MyDialog(parent);
           dialog.setVisible(true);
        }

        public MyDialog getMyDialog(){
            return dialog;
        }
    }

The dialog embeds yet another panel, and threads the frame through.

    
class MyDialog extends JDialog{

        private final YetAnotherPanel yetAnotherPanel;

        public MyDialog(JFrame parent) {
            yetAnotherPanel = new YetAnotherPanel(parent);
        }

        public YetAnotherPanel getAnotherPanel(){
            return yetAnotherPanel;
        }
    }

Notice that each embedded panel/dialog has a getter method associated with it.

The whole point to this is that we can access any node in the hierarchy from any other node. Assume we are in some other part of the GUI hierarchy. Since we have a handle to the parent frame, we can make a call like this, which in our hypothetical situation would blink some text.

        
parentFrame.getPanel().getAnotherPanel().getMyDialog().getYetAnotherPanel().blinkSomeText();

 

 

 

 

The Mediator Pattern is Really Just a Thread-Safe Singleton

To overcome a lot of the bloat that the previously described technique describes, use a thread safe singleton to hold any interested components.

public class ComponentMediator {

    private static ComponentMediator instance = new ComponentMediator();

    public static ComponentMediator getInstance() {
        return instance;
    }

    private YetAnotherPanel yetAnotherPanel;
    private AnotherPanel anotherPanel;

    public YetAnotherPanel getYetAnotherPanel() {
        return yetAnotherPanel;
    }
    public void setYetAnotherPanel(YetAnotherPanel yetAnotherPanel) {
        this.yetAnotherPanel = yetAnotherPanel;
    }
    public AnotherPanel getAnotherPanel() {
        return anotherPanel;
    }
    public void setAnotherPanel(AnotherPanel anotherPanel) {
        this.anotherPanel = anotherPanel;
    }

    //etc...

}

This singleton object contains all the components that we are interested in. This kills the bloat as we don’t need getters for all the panels, these horrible chained method calls, and passing the frame through all the constructors.

In the constructor of our interested objects we add it to the constructor like this :

    
class AnotherPanel extends JPanel {

        public AnotherPanel() {
            ComponentMediator.getInstance().setAnotherPanel(this);
            ....
        }
    }

…then in some far away panel we can use the mediator to access this panel, and call a hypothetical method called blinkSomeText().

    
class SomeFarAwayPanel extends JPanel {

        public someButtonPress() {
            ComponentMediator.getInstance().getAnotherPanel().blinkSomeText();
        }
    }

Some find the way the singleton holds references to the components is a little bloated. It is fairly bloated, and another option would be to store all these references as a map, and access the components using get and put from the map object. It’s a matter of taste.

Further reading:

Posted in Design Pattern, Swing, Swing Design Pattern | Tagged , , , | Leave a comment

JSTabbedPane: Simple Tabbed Pane for Javascript

There are a number of tabbed pane components out there in Javascript world. But most of the tabbed panes out there are provided just as a javascript library, and have been minified/obfuscated making tweaks difficult.

JSTabbedPane is a slick and simple little javascript component. You are free to modify it as you wish.

cheese

To use the component in your javascript code, have a look at the code snippet below. You basically need to define your <div>s and associated string titles, after which you then pass into the component. The tabbed panel component does the rest.

$(document.body).ready(function(){

var div1 = document.createElement('div');
div1.style.background = 'white'
div1.style.height = '200px'
div1.style.width = '900px'

var div2 = document.createElement('div');
div2.style.background = 'white'
div2.style.height = '200px'
div2.style.width = '900px'

var div3 = document.createElement('div');
div3.style.background = 'white'
div3.style.height = '200px'
div3.style.width = '900px'

var d1 = document.createElement('ul');
d1.innerHTML = "<li>audi<button>ok</button><li>VW <button>ok</button><li> Toyota<button>ok</button><li> Jeep<button>ok</button>"

var d2 = document.createElement('ol');
d2.innerHTML = "<li>Cheddar<li>Blue Vein <li> Swiss<li> Gouda"

var d3 = document.createElement('ol')
d3.innerHTML = "<li>Naomi Campbell<li>Claudia Schiffer <li> Maren Muckenthaler<li> Heidi Klum"

div1.appendChild(d1);
div2.appendChild(d2);
div3.appendChild(d3);

this.tabbedPane = Object.create(JSTabbedPane);

this.tabbedPane.addElementsToTabbedPane(
[
{   title : "Automobiles",
viewable : div1 },
{   title : "Cheeses",
viewable : div2 },
{   title : "Super Models",
viewable : div3 }
]
);

document.body.appendChild(this.tabbedPane.tpView);
this.tabbedPane.selectPage(0);
});

In the above code sample our three arbitrary divisions are passed into the addElementsToTabbedPane method. Then we append the view of the tabbed pane (the HTMLElement), onto the body of the HTML document using the line:

document.body.appendChild(this.tabbedPane.tpView);

Finally we select the first page (aka. pane) on our tabbed pane by calling this method :

this.tabbedPane.selectPage(0);

Here is the code for JSTabbedPane :

//////////////////////////////////////
//TabbedPane
//////////////////////////////////////
function JSTabbedPane() {

    this.tpView = document.createElement('div')
    this.tpView.id = 'tabbedpane';

    this.menu = new JSTabbedPaneMenu();

    this.tpView.appendChild(this.menu.menuView);

    this.addElementsToTabbedPane = function(pages) {

        for (var i=0;i < pages.length;i++) {
            var page = pages[i].viewable;

            page.name = pages[i].title;

            this.menu.addTabbedPanel(page);
        }
    };

    this.getSelectedPage = function() {
        for (var i=0;i < this.menu.tabbedPaneItemArray.length;i++) {
            if (this.menu.tabbedPaneItemArray[i].active) {
                return this.menu.tabbedPaneItemArray[i].page;
            }
        }
    }

    this.selectPage = function (i) {
        var tabbedPaneItem = this.menu.tabbedPaneItemArray[i];
        tabbedPaneItem.setItemActive(true);
    }
}

//////////////////////////////////////
//TabbedPaneMenu
//////////////////////////////////////

function JSTabbedPaneMenu() {

    this.tabbedPaneItemArray = [];

    var div = document.createElement('div');
    div.id = 'tabbedpanemenu';

    div.style.paddingTop = '5px';
    div.style.overflow = 'hidden';

    div.style.display = 'block';
    div.style.background = 'gray';

    this.menuView = div;

    /**
     * Associate HTML Element with a menu item, then append menu item to the menu.
     *
     * @param {HTMLElement} page
     */
    this.addTabbedPanel = function(page) {

        var menuItem = new JSTabbedPaneItem();

        page.style.marginTop = '35px';

        menuItem.setPage(page);
        menuItem.setMenu(this);

        //append menu item to menu
        this.menuView.appendChild(menuItem.view);

        //put it in array
        this.tabbedPaneItemArray.push(menuItem);
    };

    /**
     * @param {HTMLElement} page
     */
    this.select = function(page) {

        for (var i=0;i < this.tabbedPaneItemArray.length;i++) {

            if (page==this.tabbedPaneItemArray[i].page) {

                var item = this.tabbedPaneItemArray[i];
                item.setItemActive(true);
                this.activePage = item.page;

            }else {
                this.tabbedPaneItemArray[i].setItemActive(false);
            }
        }
    };
}

//////////////////////////////////////
//TabbedPaneItem
//////////////////////////////////////

function JSTabbedPaneItem() {
    var item = document.createElement('div');

    var self = this;

    $(item).click(function(e){
        self.menu.select(self.page);
    });

    item.style.float = 'left';

    item.style.cursor =  'pointer'

    item.style.color = '#666666';
    item.style.fontSize = '14px';
    item.style.fontFamily= 'arial';

    item.style.lineHeight = '14px';
    item.style.textDecoration = 'none';

    item.style.width = '160px';
    item.style.height = '30px';

    item.style.marginTop = '-8px'; //offset from the top
    item.style.paddingTop = '13px';

    item.style.cssFloat = 'left';

    //inner text positioning
    item.style.paddingLeft = '30px';

    this.view = item;

    this.setPage = function(page) {
        this.page = page;

        item.id = 'tabbedpane.' + this.page.name;

        item.appendChild(document.createTextNode(this.page.name));
    };

    this.setMenu = function(menu) {
        this.menu = menu;
    };
    this.setItemActive = function(active) {
        var menuView = this.menu.menuView;

        if (menuView.activePage)
            menuView.removeChild(menuView.activePage);

        this.active = active;

        var itemView = this.view;

        itemView.className = active?'active':'';
        if (active) {

            itemView.style.background = 'white';

            //has to match up with the inactive (-4 + 9) = (-8 +13)
            itemView.style.marginTop = '-4px';
            itemView.style.paddingTop = '9px';

            this.menu.activePage = this.page;

            menuView.appendChild(this.page);

        }else {

            if (this.page && this.page.parentNode == menuView)
                menuView.removeChild(this.page);

            itemView.style.background = 'gray';

            //has to match up with the inactive (-4 + 9) = (-8 +13)
            itemView.style.marginTop = '-8px';
            itemView.style.paddingTop = '13px';
        }
    };
}

Notes on JSTabbedPane

  • JSTabbedPane: The main object. Simple accessability methods.
  • JSTabbedPaneMenu: Contains an array of the menu items.
  • JSTabbedPaneMenuItem: An item is a user defined DIV connected with a title. When the setActive method is called, the old page is removed and the new page is added.
  • There is no CSS file associated with this. All CSS stylings are in the code. Its up to you to do what you want with that.
  • The menu item view object is painted with a slight offset when it is active. You can remove this feature if you wish. You can also change the colour of the tab object it is selected.Hope you like it!

    Maren Muckenthaler


Posted in JavaScript | Tagged , , , | Leave a comment
THREE_COLUMN_PAGE