Montag, 6. Juni 2011

 

Listings - Source Code drucken

Es sollte m.E. doch möglich sein, Listings in der folgenden - lesbaren - Form zu erzeugen:
Die Schrift ist leserlich, die Zeilen richtig eingerückt, Dateiname und Autor etc. sind ersichtlich (Ich gebe zu, dieses Bild ist schlecht...). Verwendet wurde recode und a2ps unter Linux. 
Wichtig ist auch, dass man allgemeine Programmierrichtlinien befolgt.
Aber mit Office kann man auch sehr schöne Listings erzeugen:
Man muss nur Kopf- und Fußzeilen einrichten, die Schrift auf Courier New (9 oder 10pt) oder eine andere mit fixer Breite und dann zwei Seiten auf eine drucken.
Ähnliches gibt es schon unter Listings (Source-Code drucken) zu lesen.

Stattdessen bekommt man so etwas.

Der Sourcecode ist unmöglich zu lesen, weil die Einrückung bald die dargestellte Zeilenlänge erreicht. Die Schriftgröße ist aber zumindest OK.

Schlimm ist aber das folgende Listing, da würden 4 Code-Seiten auf einem Blatt Platz haben. Trotzdem sind es aber nur 2 Seiten pro Blatt. Die Schrift ist unleserlich klein.

Das ist eine Zumutung!

Labels: , , , , ,


Mittwoch, 13. Januar 2010

 

Ringbuffer Simulation

Sie finden unter ringbuffer.jar eine Simulation eines Ringbuffers (bzw. Queue).


Aufrufen mit
java -jar ringbuffer.jar


Hier noch ein paar Worte zur Implementierung. Die Klasse Queue ist ganz herkömmlich implementiert. Bei einem Über- bzw. Unterlauf wird eine IndexOutOfBoundsException geworfen. Die Felder der Klasse wurden ohne private definiert, damit die Klasse GraphView im selben Paket direkt darauf zugreifen kann (erspart Schreibarbeit...).
package queuegraph;

/**
* Ringbuffer.
*
* @author Harald R. Haberstroh (hp)
*
*/
public class Ringbuffer {
String[] ring;
int usedSize = 0;
int size = 0;
int getIndex = 0;
int putIndex = 0;
final static int DEFAULTSIZE = 12;

public Ringbuffer(int size) {
this.size = size;
ring = new String[this.size];
}

public Ringbuffer() {
this(DEFAULTSIZE);
}

public void put(String c) throws IndexOutOfBoundsException {
usedSize++;
if (usedSize > size) {
throw new IndexOutOfBoundsException("Ringbufferüberlauf");
}
ring[putIndex] = c;
putIndex = (putIndex + 1) % size;
}

public String get() throws IndexOutOfBoundsException {
usedSize--;
if (usedSize < 0) {
throw new IndexOutOfBoundsException("Ringbuffer schon leer");
}
String ret = ring[getIndex];
getIndex = (getIndex + 1) % size;
return ret;
}

public int getSize() {
return size;
}

public boolean isEmpty() {
return usedSize == 0;
}
}

Die Klasse GraphView erweitert ein JPanel und "zeichnet" das Array mit Inhalt. Zusätzlich werden die "Zeiger" getIndex und putIndex durch die Position dargestellt.

Bei einem Über- bzw. Unterlauf wird der Fehler angezeigt und die Funktion "deaktiviert", indem put() und get() immer prüfen, ob error == null (also kein Fehler gesetzt ist).

Das Fenster wird in main() aufgebaut.

package queuegraph;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Rectangle2D;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

/**
* graphische Darstellung eines Ringbuffers.
*
* @author Harald R. Haberstroh (hp)
*
*/
public class GraphView extends JPanel {
private static final long serialVersionUID = -5311802173756493369L;

private Ringbuffer ringbuffer;

private String error;

/**
* Hier wird das Array mit Inhalt, die Indices und und die Ampel gezeichnet.
*
* @see javax.swing.JComponent#paintComponent(java.awt.Graphics)
*/
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int maxx = 0; // für Panelgröße
int maxy = 0;
int x = 10;
int y = 10;

// das Array
for (int i = 0, j = ringbuffer.getIndex; i < ringbuffer.usedSize; i++) {
String s = ringbuffer.ring[j];
Rectangle2D box = g.getFontMetrics().getStringBounds(s, 0, s.length(), g);
int hpos = (int) (15.0 - box.getCenterX());
int vpos = (int) (15.0 - box.getCenterY());
g.drawString(s, j * 30 + hpos + 10, y + vpos);
j = (j + 1) % ringbuffer.getSize();
x = j * 30 + 10;
if (x > maxx)
maxx = x;
if (y > maxy)
maxy = y;
}

// die Indices
x = 10;
y = 10;
for (int i = 0; i < ringbuffer.getSize(); i++) {
g.drawRect(x, y, 30, 30);
if (i == ringbuffer.getIndex) {
pfeil(g, x + 15, y + 30, "getIndex");
}
if (i == ringbuffer.putIndex) {
pfeil(g, x + 15, y + 30, "putIndex");
}
x += 30;
if (x > maxx)
maxx = x;
if (y > maxy)
maxy = y;
}

// Panelgröße
maxx += 10;
maxy += 100;
setPreferredSize(new Dimension(maxx, maxy));

// die Ampel
if (!ringbuffer.isEmpty() && ringbuffer.getIndex == ringbuffer.putIndex) {
g.setColor(Color.RED);
g.fillOval(20, maxy - 5, 10, 10);
} else if (!ringbuffer.isEmpty()) {
g.setColor(Color.YELLOW);
g.fillOval(35, maxy - 5, 10, 10);
} else {
g.setColor(Color.GREEN);
g.fillOval(50, maxy - 5, 10, 10);
}
g.setColor(getForeground());
g.drawOval(20, maxy - 5, 10, 10);
g.drawOval(35, maxy - 5, 10, 10);
g.drawOval(50, maxy - 5, 10, 10);
g.drawString("Füllstand", 80, maxy - 5 + g.getFontMetrics().getAscent());

// Fehlermeldung
if (error != null) {
g.setFont(new Font(Font.MONOSPACED, Font.BOLD, 35));
g.setColor(Color.YELLOW);
Rectangle2D box = g.getFontMetrics().getStringBounds(error, 0,
error.length(), g);
g.drawString(error, (int) (getWidth() / 2.0 - box.getCenterX()) + 2,
(int) (getHeight() / 2.0 - box.getCenterY()) - 2);
g.setColor(Color.RED);
g.drawString(error, (int) (getWidth() / 2.0 - box.getCenterX()),
(int) (getHeight() / 2.0 - box.getCenterY()));
}
}

private void pfeil(Graphics g, int x, int y, String bez) {
// FIXME: ist noch kein Pfeil, nur Linie
g.drawLine(x, y, x, y + 30);
Rectangle2D box = g.getFontMetrics().getStringBounds(bez, 0, bez.length(),
g);
int hpos = (int) (x - box.getCenterX());
int vpos = (int) (y + 30 + box.getHeight());
g.drawString(bez, hpos, vpos);
}

public GraphView() {
ringbuffer = new Ringbuffer();
}

public void put(String c) {
if (error != null)
return;
try {
ringbuffer.put(c);
} catch (IndexOutOfBoundsException e) {
error = "Ringbufferüberlauf";
}
repaint();
}

public String get() {
String ret = null;
if (error != null)
return ret;
try {
ret = ringbuffer.get();
} catch (IndexOutOfBoundsException e) {
error = "Ringbuffer schon leer";
}
repaint();
return ret;
}

static int element = 0;

/**
* Gesamtes Fenster zeichnen mit Buttons etc.
*
* @param args
*/
public static void main(String[] args) {
final GraphView view = new GraphView();
JFrame f = new JFrame("Ringbuffer");
f.setSize(500, 200);
f.setLocationRelativeTo(null);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JScrollPane pane = new JScrollPane(view);
JPanel panel = new JPanel(new BorderLayout());
panel.add(pane, BorderLayout.CENTER);
JPanel buttons = new JPanel(new FlowLayout());
JButton putButton = new JButton("put");
putButton.addActionListener(new ActionListener() {

@Override
public void actionPerformed(ActionEvent e) {
element++;
view.put("" + element);
}

});
JButton getButton = new JButton("get");
getButton.addActionListener(new ActionListener() {

@Override
public void actionPerformed(ActionEvent e) {
view.get();
}

});
buttons.add(putButton);
buttons.add(getButton);
panel.add(buttons, BorderLayout.SOUTH);
f.setContentPane(panel);
f.setVisible(true);
}
}


Der Code ist kein gutes Beispiel für Programmierstil, weil ziemlich viele Konstante für die grafische Aufbereitung einfach im Code stehen.

Labels: , ,


Mittwoch, 4. November 2009

 

Projektnamen im CVS für Abgaben

Für Abgaben nennen Sie Ihre Projekte bitte nach folgender Regel:
klasse-familienname-sprache-projektname
klasse ... 2ad, 2bd, 2cd usw.
familienname ... Ihr Familienname
sprache ... die verwendete Programmiesprache (python, java, c usw.)
projektname ... der in der Aufgabenstellung genannte Projektname

Bitte verwenden Sie nur Kleinbuchstaben.

Beispiel: 2ad-maierhofer-python-newprog

Die sprache ist optional, d.h. obiges Beispiel könnte auch so benannt werden: 2ad-maierhofer-newprog


Hinweis: falls es mehrere Personen in der Klasse gibt, die denselben Familiennamen haben, dann verwenden Sie bitte den Vornamen: klasse-familienname-vorname-sprache-projektname
Sollte auch das nicht eindeutig sein, dann verwenden Sie eine fortlaufende Nummer beim Familiennamen (der oder die ältere hat 1): klasse-familienname-1-sprache-projektname

Labels: , , , ,


Samstag, 16. Mai 2009

 

Einfaches GUI-Beispiel ohne GUI-Builder mit Grafik

gui-demo_1_2.jar (Update) ist ein Jar-Archiv mit einem kleinen Beispiel einer einfachen Java-Applikation mit Menüs und einer Zeichenfläche.
Die Menü-Einträge wurden als anonyme Actions (AbstractAction) implementiert. Im (anonymen) Konstruktor werden die Eigenschaften des Menüeintrags definiert. Als Beispiel soll der "Öffnen"-Eintrag dienen:
    Action openAction = new AbstractAction() {
{
putValue(Action.NAME, "Öffnen");
putValue(Action.DISPLAYED_MNEMONIC_INDEX_KEY, 1);
putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("control O"));
putValue(Action.SMALL_ICON, smallOpenIcon);
}

public void actionPerformed(ActionEvent e) {
JFileChooser fileChooser = new JFileChooser();
if (fileChooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
// TODO: Action File open
}

}
};

Die Menü-Eigenschaften werden durch die putValue()-Aufrufe gesetzt. Action.NAME setzt den String für das Menü. Welches Zeichen unterstrichen wird, definiert Action.DISPLAYED_MNEMONIC_INDEX_KEY, wo der Index (hier 1 für das 'f') definiert wird. Action.ACCELERATOR_KEY setzt den Shortcut. Mit dem letzten (verwendeten) Wert wird ein Icon gesetzt, welches vorher natürlich geladen werden muss.

Hier das ganze Listing der Klasse WindowWithMenu:

package gui;

import java.awt.HeadlessException;
import java.awt.event.ActionEvent;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;
import javax.swing.KeyStroke;

/**
* Applikationsfenster mit Menü.
*
* @author Harald R. Haberstroh (hh)
*
*/
public class WindowWithMenu extends JFrame {
private static final long serialVersionUID = 7874149670026759869L;

private JMenuBar menuBar;

private JMenu fileMenu;

private JMenu editMenu;

private JMenu optionsMenu;

private JMenu helpMenu;

public Action openAction;

public Action newAction;

public Action aboutAction;

public Action exitAction;

public WindowWithMenu() throws HeadlessException {
this("WindowWithMenu");
}

/**
* Ein neues Fenster mit Menü - nur ein Gerüst.
*
* @param title
* Fenstertitel
* @throws HeadlessException
* keine Maus/Bildschirm/Tastatur
*/
public WindowWithMenu(String title) throws HeadlessException {
super(title);
/** ** Öffnen */
final Icon smallOpenIcon = new ImageIcon(WindowWithMenu.class
.getResource("/images/fileopen16x16.png"));
openAction = new AbstractAction() {
{
putValue(Action.NAME, "Öffnen");
putValue(Action.DISPLAYED_MNEMONIC_INDEX_KEY, 1);
putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("control O"));
putValue(Action.SMALL_ICON, smallOpenIcon);
}

public void actionPerformed(ActionEvent e) {
JFileChooser fileChooser = new JFileChooser();
if (fileChooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
// TODO: Action File open
}

}
};
/** * Neu */
newAction = new AbstractAction() {
{
putValue(Action.NAME, "Neu");
putValue(Action.DISPLAYED_MNEMONIC_INDEX_KEY, 1);
putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("control N"));
}

public void actionPerformed(ActionEvent e) {
System.out.println("New");
}

};
/** * Exit */
exitAction = new AbstractAction() {
{
putValue(Action.NAME, "Beenden");
putValue(Action.DISPLAYED_MNEMONIC_INDEX_KEY, 0);
putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("control Q"));
}

public void actionPerformed(ActionEvent e) {
System.exit(0);
}
};

/** * Über */
aboutAction = new AbstractAction() {
{
putValue(Action.NAME, "Über das Programm");
putValue(Action.DISPLAYED_MNEMONIC_INDEX_KEY, 1);
putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("F1"));
}

public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(null, "(c) 2009, Harald R. Haberstroh",
"Über das Programm", JOptionPane.INFORMATION_MESSAGE);
}
};

reInititalize();
}

/**
* stelle "Anfangswerte" her und erzeuge Menü neu (die Actions sind public und
* daher änderbar).
*/
public void reInititalize() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(500, 400);
setLocationRelativeTo(null); // zentrieren

/*
* Menü
*/
menuBar = new JMenuBar();

/*
* Dateimenü
*/
fileMenu = new JMenu("Datei");
fileMenu.setMnemonic('D');
menuBar.add(fileMenu);

/** ** Öffnen */
fileMenu.add(openAction);

fileMenu.add(newAction);
fileMenu.addSeparator();

/** ** Beenden */
fileMenu.add(exitAction);

/*
* Bearbeiten Menü
*/
editMenu = new JMenu("Bearbeiten");
editMenu.setMnemonic('B');
menuBar.add(editMenu);

/*
* Optionen Menü
*/
optionsMenu = new JMenu("Optionen");
optionsMenu.setMnemonic('O');
menuBar.add(optionsMenu);

/*
* Hilfe Menü
*/
helpMenu = new JMenu("Hilfe");
helpMenu.setMnemonic('H');
menuBar.add(helpMenu);
setJMenuBar(menuBar);

helpMenu.add(aboutAction);

}

/**
* @param args
*/
public static void main(String[] args) {
WindowWithMenu win = new WindowWithMenu();
win.setVisible(true);
}

}

Labels: , ,


Mittwoch, 13. Mai 2009

 

Speicherlayout - Lösung

Was bedeuten die Ausgaben des Programms bzw. welchen Zweck verfolgt dieses Programm?

Die Speicheradressen und die Inhalte an diesen Adressen werden ausgegeben. Es zeigt, wie der Speicher für Variable reserviert wird.

Von Programmieren mit Harald R. Haberstroh

Man sieht, dass int Variable jeweils 4 Bytes groß sind (bei 32 Bit rechnern).

Speicher wird in der Reihenfolge der Deklaration von oben nach unten angelegt, d.h. beginnend mit höheren Speicheradressen. Bei Arrays ist der niedrigste Index jedoch an der niedrigsten Stelle des entsprechenden Speicherbereichs.

Es ist extrem wichtig diese Speicherorganisation zu verstehen, da viele Programmierfehler und Sicherheitsprobleme (Einbrüche in Web-Server, Verbreitung von Würmern und Viren) auf diesem Speicherlayout basieren.

Was bedeuten die Argumente von main ?

Die Argumente entsprechen dem from sys import argv von Python. Da in C Arrays keine Zusatzinformationen über Länge (Anzahl) enthalten, wird die Anzahl der Elemente im Parameter argc übergeben. argc enthält die Anzahl der Programmparameter (incl. Programmname), argv ist ein Array mit den Argumenten.

Labels: , ,


 

Zeigerarray - Speicherlayout

Skizzieren Sie das Speicherlayout für das folgende Programm.


 /*
* File: zeigerarray.c
*
* Zweck: Beispiel fuer Zeigerarray
*
* Autor: Harald Haberstroh
*
* Algorithmus:
* Bei diesem Beispiel wird ein Array mit konstanten Strings
* angelegt (also ein Zeigerarray), dann wird ein
* 2-dimensionales Array angelegt, das dieselben Strings
* speichern kann. Als letztes ein 2-dimensionales Array,
* welches ebenfalls mit konstanten Strings initialisiert wird.
*
* Die Speichergroessen werden ermittelt.
*
* Die Zeiger des Zeigerarrays werden veraendert.
*
* Der Inhalt wird veraendert. Beim Aendern eines konstanten
* Strings gibt es einen Segmentation Fault.
*
* Es wird noch ein Makro __LINE__ verwendet, Der Preprozessor
* (Precompiler) ersetzt dieses Makro durch die aktuelle
* Zeilennummer.
*
* History: 2002-11-03
* 2007-02-14, Umlaute...
*/

/*--------------------------- includes ------------------------------*/
#include <stdio.h>
#include <ctype.h>
#include <assert.h>
#include <string.h>

/*--------------------------- defines -------------------------------*/

/*--------------------------- typedefs ------------------------------*/

/*--------------------------- globals -------------------------------*/

/*-------------------------- prototypes -----------------------------*/

/*----------------------------- main --------------------------------*/
int main( int argc, char *argv[] )
{
int i;
int size = 0;
char *texte[] = {
"Eine Zeile Text",
"Eine 2. Zeile Text",
"Diese Art der Initialisierung kann auch fuer Fehlermeldungen "
"verwendet werden",
"Die letzte Zeile"
};
char aTexte[sizeof(texte)/sizeof(texte[0])][80] = { { 0 } };
/* 76 Zeichen braucht der laengste
* String */
char aTexteInit[sizeof(texte)/sizeof(texte[0])][80] = {
"Eine Zeile Text",
"Eine 2. Zeile Text",
"Diese Art der Initialisierung kann auch fuer Fehlermeldungen "
"verwendet werden",
"Die letzte Zeile"
};

for (i = 0; i < sizeof(aTexte) / sizeof(aTexte[0]); i++)
strcpy(aTexte[i], texte[i]); /* Die Strings auch in's 2-dim Array
* kopieren */

printf("sizeof(texte) = %d\n", sizeof(texte));
/* 4 Zeiger, der Speicher fuer die
* konstanten Strings ist hier
* nicht mitgerechnet -> 16 Bytes */
for (i = 0, size = 0; i < sizeof(texte) / sizeof(texte[0]); i++)
size += strlen(texte[i]) + 1; /* tatsaechlicher Mindestspeicher-
* verbrauch ist die Laenge des
* Strings plus 1 fuer '\0' */
printf("... tats. Speicherbedarf sizeof(texte) + "
"Laengen der Strings (%d) = %d\n", size, sizeof(texte)+size);

for (i = 0; i < sizeof(texte) / sizeof(texte[0]); i++)
printf("sizeof(texte[%d]) = %d, strlen(texte[%d]) = %d\n"
, i, sizeof(texte[i]), i, strlen(texte[i]));
/* Speicherbedarf und Laenge des
* Strings ausgeben */

printf("sizeof(aTexte) = %d\n", sizeof(aTexte));
/* Hier wird wirklich der gesamte
* Speicherbereich ausgegeben,
* das sind 4 * 80 Bytes */

for (i = 0; i < sizeof(aTexte)/sizeof(aTexte[0]); i++)
printf("sizeof(aTexte[%d]) = %d, strlen(aTexte[%d]) = %d\n"
, i, sizeof(aTexte[i]), i, strlen(aTexte[i]));
/* Speicherbedarf und Laenge des
* Strings ausgeben */

printf("sizeof(aTexteInit) = %d\n", sizeof(aTexteInit));
/* Hier wird wirklich der gesamte
* Speicherbereich ausgegeben,
* das sind 4 * 80 Bytes */

for (i = 0; i < sizeof(aTexteInit)/sizeof(aTexteInit[0]); i++)
printf("sizeof(aTexteInit[%d]) = %d, strlen(aTexteInit[%d]) = %d\n"
, i, sizeof(aTexteInit[i]), i, strlen(aTexteInit[i]));
/* Speicherbedarf und Laenge des
* Strings ausgeben */

strcpy(aTexte[2], "Eine 3. Zeile Text");
/* In das Array kann natuerlich
* etwas kopiert werden (solange
* der String nicht zu lange
* ist...) */
printf("aTexte[2] wurde geaendert in \"%s\"\n", aTexte[2]);

printf("Zeile %d: Aenderung eines Strings (aTexte[0][0] klein)\n"
, __LINE__ + 1);
aTexte[0][0] = tolower(aTexte[0][0]);
/* Anfangsbuchstabe klein, das
* darf sein, da dies ja ein
* Array ist */

for (i = 0; i < sizeof(texte) / sizeof(texte[0]); i++)
printf("texte[%d] = \"%s\"\n", i, texte[i]);

texte[2] = aTexte[2]; /* Zeiger "verbiegen", texte[2]
* zeigt nun auf das Array
* aTexte[2] */
printf("texte[2] -> aTexte[2] = \"%s\"\n", texte[2]);

printf("Zeile %d: Aenderung nun nicht mehr konstanten Strings (texte[2][0]"
" klein)\n"
, __LINE__ + 1);
texte[2][0] = tolower(texte[2][0]); /* Das sollte gehen... */
printf("texte[2] = \"%s\"\n", texte[2]);

printf("Zeile %d: Aenderung eines konstanten Strings (texte[0][0] klein)\n"
, __LINE__ + 1);
texte[0][0] = tolower(texte[0][0]); /* Anfangsbuchstabe klein in den
* konstanten Strings -> SegFault,
* da Konstante Strings nicht am
* Stack angelegt werden */
return 0;
}

Sourcecode von zeigerarray.c (Versionsnummer entfernen).

Labels: , ,


 

Speicherlayout

Geben Sie folgendes Programm ein:
#include <stdio.h>
int main( int argc, char *argv[] )
{
int a = 23;
int b = 42;
int feld[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
int i;

printf("a = %2d &a = %x (%u)\n", a,
(unsigned int)&a, (unsigned int)&a);
printf("b = %2d &b = %x (%u)\n", b,
(unsigned int)&b, (unsigned int)&b);

for (i = 0; i < sizeof(feld) / sizeof(int); i++) {
printf("feld[%d] = %2d &feld[%d] = %x (%u)\n", i, feld[i], i,
(unsigned int)&feld[i], (unsigned int)&feld[i]);
}

printf("feld = %x (%u)\n", (unsigned int)feld, (unsigned int)feld);

return 0;
}
Diskutieren Sie diese Fragestellungen in der Gruppe (2 bis 3 Personen)!

Labels: , ,


Mittwoch, 6. Mai 2009

 

Einführungsbeispiel C

Erstellen Sie ein Programm in C, welches nach Ihrem Namen, der Klasse und der Katalognummer die Größe in Bytes sowie den kleinsten und größten Wert für jeden der folgenden Datentypen ausgibt: char, int, long, short, float und double. Verwenden Sie den sizeof-Operator.

Beispielausgabe (die Minimal und Maximalwerte fehlen bei fast allen Datentypen):
Mario Maierhofer, 3DD, 23
char 1 0..255
int 4 ...
long 4 ...
short 2
float 4
double 8

Da die Größen der Datentypen vom System/Compiler abhängen, werden sich auf jedem System andere Werte ergeben.

Nennen Sie das Programm klasse-sizes-name.c (z.B. 3ad-sizes-maierhofer.c und schicken Sie es per Mail.

Labels: , ,


Donnerstag, 30. April 2009

 

Musterlösung zu Unit-Tests

Folgende Grenzfälle werden getestet:
Sonderfälle:
"Normalfälle":
Weitere Annahmen, die aus der Angabe nicht unbedingt hervorgehen:
Die Artikel (Elemente/Items) werden in der Reihenfolge und der gegebenen Anzahl gespeichert, als addItems() aufgerufen wurde. Wird ein Element mit der Anzahl 5 in den Warenkorb aufgenommen, so wird das Element 5 mal gespeichert.

Testklasse:
import java.util.Iterator;
import junit.framework.TestCase;

/**
* unittests/.ShoppingCartTest.java
*
* @author (c) 2009, Harald R. Haberstroh 23.04.2009
*/
public class ShoppingCartTest extends TestCase {

private ShoppingCart cart;

private Item[] items = { new Item(1), new Item(2), new Item(3) };

/**
* @throws java.lang.Exception
*/
protected void setUp() throws Exception {
cart = new MyCart();
}

/**
* @throws java.lang.Exception
*/
protected void tearDown() throws Exception {
}

/*
* addItem mit Menge 0, itemCount() muss gleich bleiben.
*/
public void testAddItems0() {
try {
cart.addItems(items[0], 0);
assertEquals("testDeleteItems0: 0 Elemente", 0, cart.itemCount());
} catch (NegativeCountException e) {
fail("testAddItem0: es dürfte keine NegativeCountException geworfen werden");
}
}

/*
* eine negative Anzahl von Elementen aufnehmen -> NegativeCountException
*/
public void testAddItemNegativ() {
try {
cart.addItems(items[0], -23);
fail("testAddItemNegativ: es müsste eine NegativeCountException geworfen werden");
} catch (NegativeCountException e) {
}
}

/*
* deleteItem mit Menge 0, itemCount() muss gleich bleiben.
*/
public void testDeleteItems0() {
try {
cart.addItems(items[0], 1);
cart.deleteItems(items[0], 0);
assertEquals("testDeleteItems0: 1 Element", 1, cart.itemCount());
} catch (NegativeCountException e) {
fail("testDeleteItems0: es dürfte keine NegativeCountException geworfen werden");
} catch (NoSuchItemException e) {
fail("testDeleteItems0: es dürfte keine NoSuchItemException geworfen werden");
}
}

/*
* deleteItem nicht gültiges Item -> NoSuchItemException, itemCount() == 0
*/
public void testDeleteItemsNoSuchItem() {
try {
cart.addItems(items[0], 1);
cart.deleteItems(items[1], 1);
fail("testDeleteItemsNoSuchItem: es müsste NoSuchItemException geworfen werden");
} catch (NegativeCountException e) {
fail("testDeleteItemsNoSuchItem: es dürfte keine NegativeCountException geworfen werden");
} catch (NoSuchItemException e) {
assertEquals("testDeleteItemsNoSuchItem: 1 Element", 1, cart.itemCount());
}
}

/*
* eine negative Anzahl von Elementen aufnehmen -> NegativeCountException
*/
public void testDeleteItemNegativ() {
try {
cart.addItems(items[0], -23);
fail("testAddItemNegativ: es müsste eine NegativeCountException geworfen werden");
} catch (NegativeCountException e) {
}
}

/*
* addItems für 10 Items, keine Exception, itemCount() == 10
*/
public void testAddItems10() {
try {
cart.addItems(items[0], 3);
cart.addItems(items[1], 3);
cart.addItems(items[2], 4);
assertEquals("testAddItems10: 10 Elemente", 10, cart.itemCount());
} catch (NegativeCountException e) {
fail("testAddItems10: es dürfte keine NegativeCountException geworfen werden");
}
}

/*
* addItems für 10 Items, deleteItems für 5 Items, keine Exception,
* itemCount() == 5
*/
public void testAddDeleteItems5() {
try {
cart.addItems(items[0], 3);
cart.addItems(items[1], 3);
cart.addItems(items[2], 4);
cart.deleteItems(items[0], 2);
cart.deleteItems(items[2], 2);
cart.deleteItems(items[1], 1);
assertEquals("testAddDeleteItems5: 5 Elemente", 5, cart.itemCount());
} catch (NegativeCountException e) {
fail("testAddDeleteItems5: es dürfte keine NegativeCountException geworfen werden");
} catch (NoSuchItemException e) {
fail("testAddDeleteItems5: es dürfte keine NoSuchItemException geworfen werden");
}
}

/*
* addItems für 10 Items, Iterator muss Zugriff auf die 10 Elemente gestatten
*/
public void testIterator() {
try {
cart.addItems(items[0], 3);
cart.addItems(items[1], 3);
cart.addItems(items[2], 4);
Iterator<Item> iterator = cart.iterator();
int cntItems = 0;
Item[] stroredItems = new Item[10];
while (iterator.hasNext()) {
stroredItems[cntItems] = iterator.next();
cntItems++;
}
assertEquals("testIterator: 10 Elemente", 10, cntItems);
for (int i = 0; i < 3; i++) {
assertEquals("testIterator: Element sollte gleich sein", items[0],
stroredItems[i]);
System.out.println(stroredItems[i]);
}
for (int i = 3; i < 6; i++) {
assertEquals("testIterator: Element sollte gleich sein", items[1],
stroredItems[i]);
System.out.println(stroredItems[i]);
}
for (int i = 6; i < 10; i++) {
assertEquals("testIterator: Element sollte gleich sein", items[2],
stroredItems[i]);
System.out.println(stroredItems[i]);
}
} catch (NegativeCountException e) {
fail("testIterator: es dürfte keine NegativeCountException geworfen werden");
}
}
}

Implementierung des Interfaces ShoppingCart:
import java.util.ArrayList;
import java.util.Iterator;

/**
* unittests/.MyCart.java
* @author (c) 2009, Harald R. Haberstroh
* 23.04.2009
*/
public class MyCart implements ShoppingCart {

private ArrayList<Item> items = new ArrayList<Item>();

/*
* (non-Javadoc)
*
* @see ShoppingCart#addItems(Item, int)
*/
public void addItems(Item anItem, int quantity) throws NegativeCountException {
if (quantity < i =" 0;" i =" 0;"> iterator() {
return items.iterator();
}

}

Zusätzlich müssen natürlich die geforderten Exceptions implementiert werden (Kommentare wurden hier weggelassen):
public class NegativeCountException extends Exception {

}

public class NoSuchItemException extends Exception {

}

Die Klasse für die Artikel (Item) wurde einfach durch eine Nummer ergänzt, die beim Konstruktor angegeben werden muss. Für eine realistische Anwendung müsste diese Klasse noch erweitert werden:
/**
* unittests/.Item.java
* @author (c) 2009, Harald R. Haberstroh
* 23.04.2009
*/

/**
* @author Harald R. Haberstroh (user)
*
*/
public class Item {
private int itemNr = 0;

/**
* @param itemNr
*/
public Item(int itemNr) {
this.itemNr = itemNr;
}

/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Item " + itemNr;
}

}

Labels: , ,


Mittwoch, 22. April 2009

 

Beispiel zu Unit-Tests

In einem Online-Shop wird unter anderem ein Warenkorb benötigt. Schreiben Sie Unit-Tests zu folgendem Interface (welches Sie natürlich auch implementieren müssen):

import java.util.Iterator;

/**
* @author (c) 2009, Harald R. Haberstroh 23.04.2009
*/
public interface ShoppingCart {

/**
* legt die übergebene Anzahl von Elementen in den Warenkorb
*
* @param anItem
* Element
* @param quantity
* Anzahl
* @throws NegativeCountException
* negative Anzahl gibt's nicht
*/
public void addItems(Item anItem, int quantity) throws NegativeCountException;

/**
* entfernt die Anzahl von Elementen vom Warenkorb.
*
* @param anItem
* welches Element
* @param quantity
* Anzahl
* @throws NegativeCountException
* negative Anzahl gibt's nicht
* @throws NoSuchItemException
* das Element existiert nicht im Warenkorb
*/
public void deleteItems(Item anItem, int quantity)
throws NegativeCountException, NoSuchItemException;

/**
* wieviele Element gibt's überhaupt
*
* @return Anzahl Elemente
*/
public int itemCount();

/**
* Iterator für alle Elemente (siehe Collection-API).
*
* @return Iterator über alle Elemente
*/
public Iterator<Item> iterator();
}
Denken Sie bei der Erstellung der Tests an Grenzfälle, Sonderfälle und erst dann an den "Normalfall".

Nennen Sie das Projekt klasse-unittests-name (z.B. 3ad-unittests-haberstroh) und checken Sie es am CVS ein (Achtung 3BD: packen Sie das Projekt in ein jar 3bd-unittests-name.jar und schicken Sie es per Mail an mich).

Abgabetermin 29.04.2009

Labels: , ,


Mittwoch, 25. März 2009

 

JTable

Erstellen Sie ein einfaches Java-Programm, welches einfache CSV-Dateien erzeugen, darstellen und ändern kann. Verwenden Sie zur Darstellung der CSV-Datei eine JTable. Beim Neuanlegen wir die Anzahl der Spalten festgelegt. Beim Laden einer CSV-Datei wird die Anzahl der Zeilen/Spalten durch die Datei gegeben.
Das Programm soll es ermöglichen, jede Zelle (definiert durch Zeile und Spalte) zu ändern.
Sehen Sie eine Möglichkeit zum Einfügen neuer Zeilen vor.
Eine markierte Zeile soll gelöscht werden können.

Nennen Sie das Projekt klasse-tabelle-name (klasse ist Ihre Klasse, name ist Ihr Familienname, z.B. 3bd-tabelle-mueller)

Hier finden Sie zwei Beispielklassen, TableFrame für das GUI und MyTableModel für das Tabellenmodell.


/**
* JTableDemo/at.haberstroh.table/TableFrame.java
*
* Einfaches Tabellenbeispiel mit eigenem Modell. Einfügen/Löschen von Zeilen,
* Ändern von Zeilen.
*
* (c) 2007 Harald R. Haberstroh, www.haberstroh.at
*
* 15.01.2007
*/

package at.haberstroh.table;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;

/**
* @author hp
*
*/
public class TableFrame extends JFrame {

private static final long serialVersionUID = 9118987994549780691L;
private JTable table;
private MyTableModel model;

public TableFrame() {
setLayout(new BorderLayout());
table = new JTable();
model = new MyTableModel();
table.setModel(model);
table.setPreferredScrollableViewportSize(new Dimension(550, 100));
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
add(new JScrollPane(table), BorderLayout.CENTER);
JButton eBut = new JButton("Einfügen");
JButton lBut = new JButton("Löschen");
JButton aBut = new JButton("Anzeigen");
JPanel pan = new JPanel();
pan.add(eBut);
pan.add(aBut);
pan.add(lBut);
eBut.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int zeile = table.getSelectedRow();
if (zeile > -1)
model.insertRow(zeile);
}
});
aBut.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.print(model);
}
});
lBut.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int zeile = table.getSelectedRow();
if (zeile > -1)
model.deleteRow(zeile);
}
});
add(pan, BorderLayout.SOUTH);
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
pack(); // GUI so klein als möglich machen
}

/**
* @param args
*/
public static void main(String[] args) {
new TableFrame().setVisible(true);
}

}



/**
* JTableDemo/at.haberstroh.table/MyTableModel.java
*
* Implementiert ein einfaches Modell einer 3-spaltigen Tabelle.
*
* (c) 2007 Harald R. Haberstroh, www.haberstroh.at
*
* 15.01.2007
*/
package at.haberstroh.table;

import javax.swing.table.AbstractTableModel;

/**
* @author hp
*
*/
public class MyTableModel extends AbstractTableModel {

private static final long serialVersionUID = -8833545818864673421L;

// Kopf
private Object[] name = { "Name", "Erfindung", "geboren" };

// (Anfangs-)Daten
private Object[][] daten = {
{ "C. Babbage", "The Analytical Engine", new Integer(1792) },
{ "N. Chomsky", "Die Chomsky-Grammatiken", new Integer(1928) },
{ "G.W. Leibniz", "Das Dualsystem", new Integer(1646) },
{ "A.M. Turing", "Die Turing-Maschine", new Integer(1912) },
{ "K. Zuse", "Der 1. Relaisrechner Z1", new Integer(1910) } };

/**
* @return Beschriftung
*/
public String getColumnName(int col) {
return (String) name[col];
}

/**
* @return liefert Klasse der Spalte
*/
public Class getColClass(int col) {
if (col == 0 || col == 1)
return String.class;
else
return Integer.class;
}

/**
* @return liefert Anzahl der Spalten
*/
public int getColumnCount() {
return 3;
}

/**
* @return liefert Anzahl der Zeilen
*/
public int getRowCount() {
return daten.length;
}

/**
* @param arg0 Zeile
* @param arg1 Spalte
* @return liefert Wert der Zelle
*/
public Object getValueAt(int arg0, int arg1) {
return daten[arg0][arg1];
}

/**
* @param aValue Wert der gesetzt werden soll
* @param arg0 Zeile
* @param arg1 Spalte
*/
public void setValueAt(Object aValue, int row, int col) {
daten[row][col] = aValue;
fireTableCellUpdated(row, col);
}

/**
* @return ist die Zelle änderbar?
*/
public boolean isCellEditable(int row, int col) {
return row < daten.length && col < 3;
}

/**
* Einfügen einer Zeile
* @param row die Zeilennummer vor der eingefügt wird
*/
public void insertRow(int row) {
Object[][] tmp = new Object[daten.length + 1][3];
for (int i = 0; i < row; i++) {
for (int j = 0; j < 3; j++) {
tmp[i][j] = daten[i][j];
}
}
// neue Zeile
tmp[row][0] = "";
tmp[row][1] = "";
tmp[row][2] = new Integer(0);
// Rest
for (int i = row + 1; i < tmp.length; i++) {
for (int j = 0; j < 3; j++) {
tmp[i][j] = daten[i - 1][j];
}
}
// neue daten
daten = tmp;

// update
fireTableDataChanged();
}

/**
* löschen der Zeile. Bei der letzten Zeile wird eine leere
* Zeile angelegt.
* @param row Zeilennummer
*/
public void deleteRow(int row) {
if (row >= 0 && row < daten.length && daten.length > 1) {
Object[][] tmp = new Object[daten.length - 1][3];
for (int i = 0; i < row; i++) {
for (int j = 0; j < 3; j++) {
tmp[i][j] = daten[i][j];
}
}
for (int i = row; i < tmp.length; i++) {
for (int j = 0; j < 3; j++) {
tmp[i][j] = daten[i+1][j];
}
}
// neue daten
daten = tmp;

} else { // leere Zeile
daten = new Object[][] { { "", "", new Integer(0) } };
}

// update
fireTableDataChanged();
}

/**
* @return liefert Tabelle als String
*/
public String toString() {
StringBuffer buf = new StringBuffer(1000);
for (int i = 0; i < daten.length; i++) {
for (int j = 0; j < 3; j++) {
buf.append(daten[i][j]);
}
buf.append("\n");
}
return buf.toString();
}

}

Labels: , , ,


Donnerstag, 19. März 2009

 

Kleines Java-Quiz

Hier finden Sie ein paar Java-Quiz:
Ich werde die Liste im Laufe der Zeit vervollständigen.

Viel Spaß!

Labels: , , ,


Mittwoch, 11. März 2009

 

Internationalisierung in Java

Im Beispiel Internationalisierung.zip wird demonstriert, wie man die Ein-/Ausgabe sowie eigene Texte internationalisiert.


private String resource = "international.texte"; // Package + Prefix
private ResourceBundle bundle;
private Locale locale = Locale.getDefault();
public International() {
this.bundle = ResourceBundle.getBundle(this.resource);
System.out.println(this.locale.getDisplayName());
}
public International(Locale locale) {
this.bundle = ResourceBundle.getBundle(this.resource, locale);
this.locale = locale;
System.out.println(this.locale.getDisplayName());
}

public void sayHello() {
System.out.println(this.bundle.getString("hallo"));
}

Dieses Code-Fragment zeigt einen Ausschnitt aus der Beipielklasse International. Beim Defaultkonstruktor wird als Sprache die Systemeinstellung verwendet, der zweite Konstruktor wird mit einer Locale aufgerufen:


International i = new International(new Locale("de", "AT"));
i.sayHello();

ResourceBundle.getBundle(this.resource, locale) versucht eine Datei international.texte_locale.properties zu lesen (international ist hier das Package). locale setzt sich in obigem Beispiel aus de_AT zusammen.
sayHello() gibt dann die Übersetzung zum String hallo aus, falls diese Property existiert.

Eine solche Property-Datei enthält Zeilen der Art

 msg = Übersetzung

Z.B. international.texte_de_AT.properties enthält:


# österreichische Texte
hallo = Servus
fehler = Des is foisch
zahl = Zoi
parse = des kaunst net umwaundln
io = des geht net eine oder aussi
null = Nix geht mehr (null pointer exception)

Die Methoden readNumber() bzw. printNumber() zeigen, wie man Zahlen lokalisiert einliest bzw. ausgibt.

Siehe auch Internationalisierung (Softwareentwicklung)

Labels: ,


 

CRISP, ANT und JAR

Zubereitung von CRISP Builds

Ein Build ist eine Reihe von Schritten, die Ihre kreativen Artefakte (Source Code) in auslieferbare Software transformieren.

CRISP bedeutet

vollständig (engl. Complete)
wiederholbar (engl. Repeatable)
informativ (engl. Informative)
planbar (engl. Schedulable)
portabel (engl. Portable)

Ein CRISP Build versetzt uns in die Lage jederzeit automatisch den Quellcode in auslieferbare Software zu transferieren.

Complete - vollständig heißt, dass alles von Grund auf nach dem Rezept neu erstellt wird. Es braucht nichts vor oder nach dem Build hinzugefügt werden.
Repeatable wiederholbar heißt, dass jederzeit auch frühere Softwareversionen erzeugt werden können. Der Schlüssel dazu liegt in der Versionsverwaltung (CVS).
Informative - informativ bedeutet, dass wir sofort wissen, wenn der Build schief gegangen ist. Der Build war erfolgreich, wenn die Software fehlerfrei übersetzt werden konnte und die automatischen Test bestanden hat.
Schedulable - planbar oder zeitgesteuert - ist ein Build vollständig und wiederholbar, so kann er auch automatisch z.B. zu bestimmten Zeiten angestoßen werden. Der Vorteil ist, dass man nebenher am Quellcode weiterarbeiten kann.
Portable - portable Builds bedeuten weniger, dass Builds, die auf einem Unix durchgeführt werden, auch unter Windows durchgeführt werden können. Jedoch soll der Build ohne Probleme auf einem andern Unix-System laufen. Abhängigkeiten von bestimmten IP-Adressen oder IDE sind zu vermeiden.

Der "Compile"-Knopf ist kein Build Prozess

So verlockend es auch sein mag, ihn als solchen zu benutzen, der "Compile"-Knopf ist kein Build-Prozess, der einen CRISP-Build erzeugt. Sie müssten sich auf eine einzige IDE mit einer Standardkonfiguration einigen (Einigung durch Armdrücken? - was ist die beste IDE?). Das erfordert Disziplin, denn jede Änderung der Einstellung in der IDE müsste an alle verteilt werden (CVS).

Wie automatisiert man den Build-Prozess nun mit der IDE? Ein Programmierer drückt auf den Knopf, wenn ihm eine Banane (oder ein Bier :-) hingehalten wird?

Mehr Flexibilität erhält man, durch die Verwendung eines aus jeder IDE ausgelagerten Build-Prozesses.

ANT

another neat tool - ANT ist ein auf Java-Projekte spezialisiertes Open-Source-Build-Werkzeug, welches XML-Dateien zur Steuerung verwendet. Einige IDEs können ANT-Dateien erzeugen bzw. ANT für den Build-Prozess verwenden ("Compile"-Knopf mit ANT im Hintergrund). ANT versetzt uns in die Lage CRISP-Builds zu machen.

Vorteile

- ANT-Dateien sind portabel und da ANT in Java geschrieben wurde, ist auch der Build-Prozess portabel.
- ANT verfolgt Abhängigkeiten (ähnlich make). Es ruft javac nur auf, wenn sich eine Datei geändert hat (das macht make auch, aber es bei Java effizienter, da alles in einer JVM läuft, bei make würde für jden javac -Aufruf eine JVM gestartet werden müssen).
- ANT beinhaltet neben dem Kompilieren noch eine Reihe von Tasks zur Erledigung verschiedener Dinge (z.B. JUnit -Tests). Außerdem können eigene in Java geschriebene Tasks verwendet werden.

Nachteile

Das Build-Rezept muss in einer XML-Datei ausgedrückt werden. All die eckigen XML-Klammern können verschleiern, was wirklich vor sich geht.

Eine ANT-Build Datei build.xml


<?xml version="1.0" encoding="UTF-8"?>
<project basedir="." default="build" name="AntDemo">
<property name="JUNIT_HOME" value="vendor">
<property name="build.dir" location="build">
<property name="doc.dir" location="doc">
<property name="src.dir" location="src">
<property name="vendor.dir" location="vendor">

<path id="project.classpath">
<pathelement location="bin">
<pathelement location="build">
<pathelement location="${JUNIT_HOME}/junit.jar">
</path>

<!-- Initialisierungen -->
<target name="init">
<mkdir dir="bin">
<mkdir dir="build">
</target>

<!-- löschen der binaries -->
<target name="clean">
<delete dir="bin">
<delete dir="build">
</target>

<!-- Übersetzen -->
<target depends="init" name="compile" description="Übersetzen">
<javac destdir="bin">
<src path="src">
<classpath refid="project.classpath">
</javac>
</target>

<!-- Unit Tests, vorher evtl. Übersetzen -->
<target depends="init,compile" name="test" description="Testen">
<junit fork="yes" printsummary="withOutAndErr">
<formatter type="plain">
<test name="antdemo.DatumTest">
<classpath refid="project.classpath">
</junit>
</target>

<!-- erzeuge jar-File, vorher evtl. übersetzen, Test-Klassen werden nicht
ausgeliefert -->
<target depends="init,compile" name="build" description="Build jar">
<jar destfile="build/antdemo.jar" basedir="bin" excludes="**/*Test.class">
<manifest>
<!-- erzeuge Minifest ... -->
<attribute name="Main-Class" value="antdemo.Datum">
</manifest>
</jar>
</target>
</project>

junit.jar (die für die Unit-Tests nötige Bibliothek) ist in diesem Beispiel im Verzeichns vendor zu finden. Die jar-Datei wird im Verzeichnis build erstellt. Die übersetzten Klassendateien sind im Verzeichnis bin.

Aufruf: ant [task]

Wird kein task angegeben, so wird der Default-Task ausgeführt (im Projekt definiert: <project basedir="." default="build" name="AntDemo">). Abhängigkeiten werden durch depends= definiert.

Erzeugen von jar-Dateien und einbinden fremder jar-Dateien

Oft benötigt man weitere jar Dateien. Diese müssten beim ausgelieferten Programm entweder im Class-Path sein oder man packt die jar Datei aus und im Ziel- jar wieder ein.


<?xml version="1.0" encoding="UTF-8"?>
<project basedir="." default="build" name="AntDemo">
<property name="JUNIT_HOME" value="vendor">
<property name="build.dir" location="build">
<property name="doc.dir" location="doc">
<property name="src.dir" location="src">
<property name="vendor.dir" location="vendor">

<path id="project.classpath">
<pathelement location="bin">
<pathelement location="build">
<pathelement location="${JUNIT_HOME}/junit.jar">
</path>

<!-- Initialisierungen -->
<target name="init">
<mkdir dir="bin">
<mkdir dir="build">
</target>

<!-- löschen der binaries -->
<target name="clean">
<delete dir="bin">
<delete dir="build">
</target>

<!-- Übersetzen -->
<target depends="init" name="compile" description="Übersetzen">
<javac destdir="bin">
<src path="src">
<classpath refid="project.classpath">
</javac>
</target>

<!-- Unit Tests, vorher evtl. Übersetzen -->
<target depends="init,compile" name="test" description="Testen">
<java fork="yes" classname="antdemo.DatumTest">
<classpath refid="project.classpath">
</java>
<!--
<junit fork="yes" printsummary="withOutAndErr">
<formatter type="plain">
<test name="antdemo.DatumTest">
<classpath refid="project.classpath">
</junit>
-->
</target>

<!-- erzeuge jar-File, vorher evtl. übersetzen, Test-Klassen werden nicht
ausgeliefert -->
<target depends="init,compile" name="build" description="Build jar">
<jar destfile="build/antdemo.jar" basedir="bin" excludes="**/*Test.class">
<manifest>
<!-- erzeuge Manifest ... -->
<attribute name="Main-Class" value="antdemo.Datum">
</manifest>
</jar>
</target>

<!-- erzeuge jar-File incl. junit, vorher evtl. übersetzen, Test-Klassen werden nicht
ausgeliefert
junit.jar wird zuerst ins bin ausgepackt und dann ins jar wieder
eingepackt -->
<target depends="init,compile" name="testbuild" description="Build jar">
<unjar src="vendor/junit.jar" dest="bin">
<jar destfile="build/antdemo.jar" basedir="bin">
<manifest>
<!-- erzeuge Manifest ... -->
<attribute name="Main-Class" value="antdemo.DatumTest">
</manifest>
</jar>
</target>
</project>

Das Ziel testbuild (das letzte) enthält zuerst einen Task , der junit.jar auspackt. Bei diesem Beispiel wird eingenommen, dass im Projektverzeichnis ein Unterverzeichnis vendor existiert, in dem eine Kopie von junit.jar zu finden ist.

junit.jar wird nach bin ausgepackt (dort landen alle *.class Dateien) und dann gemeinsam mit den eigenen Klassen zu build/antdemo.jar verpackt. <manifest...> erstellt das nötige Manifest, um die Datei mit java -jar antdemo.jar aufrufen zu können.

Beim Ziel build (das vorletzte) werden die Testklassen exkludiert und eine andere Startklasse ins Manifest eingetragen.

Das gesamte Beispielprojekt finden Sie hier

Die Verzeichnisstruktur ist an Projektstruktur angelehnt.

Weitere Informationen

ant.apache.org
Wikipedia Ant
projektstruktur.zip

Labels: ,


Donnerstag, 26. Februar 2009

 

Array sortieren

Man kann ein Array von einem bestimmten Typ leicht mit der Methode sort() aus der Klasse java.util.Arrays sortieren, wenn die Objekte das Interface Comparable implementiert. Dieses Interface verlangt, dass eine Methode

public abstract int compareTo(Object other);
implementiert wird. Diese Methode wird verwendet, um Objekte miteinander zu vergleichen. Der Typ Object wird dann für die konkrete Implementierung durch den benötigten Datentyp ersetzt (wobei man natürlich auch bei Object bleiben könnte, da alle Klassen von Object erben). compareTo() liefert 0, wenn die Objekte "gleich" sind, einen Wert kleiner 0, wenn das aktuelle Objekt "kleiner" als das andere (other) ist und einen Wert größer 0, wenn das aktuelle Objekt "größer" als das andere ist.
String implementiert dieses Interface bereits:

String[] arr = { "hallo", "du", "da", "wo", "bist", "du" };
java.util.Arrays.sort(arr);
System.out.println(java.util.Arrays.toString(arr));

liefert folgende Ausgabe:

[bist, da, du, du, hallo, wo]


Will man Objekte eines eigenen Typs sortieren, dann sieht das für Objekte mit einer Nummer und einem String etwa so aus:

class Data implements Comparable<Data> {
int nr;
String name;
Data(int nr, String name) {
this.nr = nr;
this.name = name;
}
public String toString() {
return String.format("%d -> %s", nr, name);
}
public int compareTo(Data other) {
int ret = nr - other.nr; // Nummer vergleichen
if (ret == 0) { // nun auch nach String vergleichen
ret = name.compareTo(other.name);
}
return ret;
}
}

Data[] data = {
new Data(23, "Thomas"), new Data(1, "Alfred"), new Data(2, "Brigitte"),
new Data(23, "Harald"), new Data(12, "Manfred"),
};
java.util.Arrays.sort(data); // SORTIEREN
System.out.println(java.util.Arrays.toString(data));

Arrays.toString(array) gibt den Inhalt eines Arrays aus. Daher wird bei obigem Codestück folgendes ausgegeben:

[1 -> Alfred, 2 -> Brigitte, 12 -> Manfred, 23 -> Harald, 23 -> Thomas]

Das Array wurde sortiert.

Labels: , , ,


Mittwoch, 25. Februar 2009

 

Killer Tux

Labels: , , , ,


Freitag, 20. Februar 2009

 

String mit Umlauten in Bytes umwandeln

UTF-8 ist eine Kodierung für UNICODE. Damit sind alle Zeichen darstellbar. UTF-8 benötigt nicht für alle Zeichen die gleiche Anzahl Bytes, so werden die Zeichen des ASCII-Codes mit einem Byte dargestellt. Viele Zeichen (z.B. Umlaute und 'ß') benötigen 2 Bytes, noch viel mehr (z.B. Chinesische Zeichen) benötigen 3 Bytes.
Die Umwandlung in Bytes ergibt daher Probleme. Probieren Sie folgenden Code im Scrapbook (eclipse):



// UTF-8 Problem mit Umlauten

// ohne encoding umwandeln
byte[] bytes = "AÄÖÜäöüßA".getBytes();
System.out.printf("%d bytes (Länge 9)\n", bytes.length);
for (byte b : bytes) {
System.out.printf("%02x ", b);
}
System.out.println(new String(bytes));

Für deutschen Zeichensatz genügt ein Encoding, welches über eine Tabelle jedem Zeichen aus unserem Zeichensatz nur ein Byte zuordnet. Wir können z.B. ISO-8859-1 verwenden:



// mit encoding umwandeln
byte[] bytes = "AÄÖÜäöüßA".getBytes("ISO-8859-1");
System.out.printf("%d bytes (Länge 9)\n", bytes.length);
for (byte b : bytes) {
System.out.printf("%02x ", b);
}
System.out.println(new String(bytes, "ISO-8859-1"));

Scrap Book zum runterladen (Achtung UTF-8)

Weitere Informationen

unterstützte encodings in Java
Unicode for Programmers
Java Tutorials - Converting Non-Unicode Text

Konvertierung von/nach C-Strings

Ist nach obigem Muster möglich. C-Strings sind Byte-Arrays mit einem Zeichen in einem Byte (kein Unicode!). Das Ende des Strings wird mit einem Nullzeichen markiert ('\0'). Dieses Byte muss man als letztes Zeichen zum C-String hinzufügen, wenn man einen C-String erzeugt bzw. entfernen, wenn man einen Java-String in einen C-String umwandeln will. Man muss dabei beachten, dass C-Strings damit eine maximale Länge haben. Die Länge ist durch die Arraygröße - 1 definiert (Null-Zeichen).

Labels: , , , ,


Donnerstag, 19. Februar 2009

 

Swing und AWT mischen

Schüler zeigen mir immer wieder ihre Programme, die folgendes Verhalten an den Tag legen: das Menü verschwindet hinter den Steuerelementen:
Obiges Bild zeigt das Problem. Das geöffnete Menü verschwindet hinter dem TextField, ist aber vor dem unteren Textfeld. Das Verhalten des unteren Textfeldes passt, jenes des oberen passt nicht.
Es handelt sich hier um ein Swing-Formular, das mit Netbeans erstellt wurde. Für das obere Textfeld wurde AWT benutzt.
Für das untere Textfeld wurde Swing verwendet (Swing Controls).

Wichtig: bei Swing-Anwendungen immer Swing-Steuerelemente verwenden (beginnen alle mit J, also JFrame, JList, usw.). Bei AWT-Anwendungen nur AWT-Steuerelemente verwenden (Frame, List, usw.).

Der Grund liegt in der unterschiedlichen "Zeichenfunktion", AWT verwendet paint(), Swing immer paintComponent().

Bei den Variablendeklarationen sieht man das auch:

private javax.swing.JMenuItem jMenuItem1;
private javax.swing.JMenuItem jMenuItem2;
private javax.swing.JMenuItem jMenuItem3;
private javax.swing.JTextField jTextField1;
private javax.swing.JPanel mainPanel;
private javax.swing.JMenuBar menuBar;
private javax.swing.JProgressBar progressBar;
private javax.swing.JLabel statusAnimationLabel;
private javax.swing.JLabel statusMessageLabel;
private javax.swing.JPanel statusPanel;
private java.awt.TextField textField1;


Die letzte Zeile enthält den "Übeltäter".

Will man "selbst" in einem Panel zeichnen, so macht man dies bei AWT mit:

import java.awt.Frame;
public class Fenster extends Frame {
//...
@Override
public void paint(Graphics g) {
//...
g.drawString("test", x, y);
//...
}

//...
}

Bei Swing (JPanel) muss man hingegen so vorgehen:

import javax.swing.JPanel;
public class GraphView extends JPanel {
//...
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int maxx = 0; // für Panelgröße
int maxy = 0;
//...
g.drawString("test", x, y);
//...
}
//...
}

Beachten Sie, dass hier unbedingt super.paintComponent(g); aufgerufen werden muss.

Labels: ,


Mittwoch, 11. Februar 2009

 

einfaches JList-Beispiel

Listen mit der Swing-Komponente JList sind etwas umständlich:
  1. muss man die JList in eine ScrollPane verpacken, damit man bei langen Listen Scrollen kann.
  2. muss man sich um das "ListModel" kümmern. Das ListModel dient zur Verwaltung von Daten in der Liste.
Es folgt ein einfaches Beispiel, welches in der Listbox Teststrings anzeigt. Mit einem Button kann man weitere Elemente entweder hinten anhängen (kein Element selektiert) oder vor der Selektion einfügen. Mit dem anderen Button kann man die gewählten Elemente löschen.
Hier der Source Code (eine Hilfsklasse List wird für die Listbox verwendet):

/**
* java-jlist: swing.Fenster.java
*
* 11.02.2009, Harald R. Haberstroh
*/

package swing;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.GraphicsConfiguration;
import java.awt.HeadlessException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

/**
* Einfache Demo einer JList mit Einfügen und entfernen von Elementen.
*
* @author Harald R. Haberstroh (hp)
*
*/
public class Fenster extends JFrame {
private static final long serialVersionUID = -273011863852896510L;
private JPanel panel; // Hauptpanel
private JScrollPane listPane; // damit JList scrollfähig ist
private JPanel buttons; // eine Pabnel für die Buttons
private List list; // unsere Liste
private JButton addButton; // Button zum Einfügen
private JButton removeButton; // Button zum Löschen
private int nrElementsAdded = 0; // für die Testdaten

private void initialize() {
// Panel in die ContentPane des Fensters setzen
panel = new JPanel();
setContentPane(panel);

// Liste erzeugen
list = new List();
listPane = new JScrollPane(list); // verpacken in ScrollPane

// Layout für Hauptpane
panel.setLayout(new BorderLayout());
panel.add(listPane, BorderLayout.CENTER);

// die Buttons...
buttons = new JPanel();
buttons.setLayout(new FlowLayout());
addButton = new JButton("Add Element");
addButton.addActionListener(new ActionListener() {

@Override
public void actionPerformed(ActionEvent e) {
nrElementsAdded++;
if (list.getSelectedIndex() < 0) { // hinten einfügen
list.getContents().addElement("test " + nrElementsAdded);
} else { // an selektierter Stelle einfügen
list.getContents().add(list.getSelectedIndex(),
"test " + nrElementsAdded);
}
list.clearSelection();
}

});
buttons.add(addButton);

removeButton = new JButton("Remove Element(s)");
removeButton.addActionListener(new ActionListener() {

@Override
public void actionPerformed(ActionEvent e) {
// alle selektierten Elemente
int[] indices = list.getSelectedIndices();
for (int index : indices) {
list.getContents().remove(index);
}
list.clearSelection();
}

});
buttons.add(removeButton);

// Buttonleiste ins Panel...
panel.add(buttons, BorderLayout.SOUTH);

// packe es auf brauchbare Größe
pack();

// Fenster schließen lassen (ohne Handler :-)
setDefaultCloseOperation(EXIT_ON_CLOSE);

// Zentriert am Bildschirm
setLocationRelativeTo(null);

// ein paar Elemente in die Liste einfügen
for (; nrElementsAdded < 10; nrElementsAdded++) {
list.getContents().addElement("test " + nrElementsAdded);
}
}

/**
* @throws HeadlessException
*/
public Fenster() throws HeadlessException {
super();
initialize();
}

/**
* @param gc
*/
public Fenster(GraphicsConfiguration gc) {
super(gc);
initialize();
}

/**
* @param title
* @param gc
*/
public Fenster(String title, GraphicsConfiguration gc) {
super(title, gc);
initialize();
}

/**
* @param title
* @throws HeadlessException
*/
public Fenster(String title) throws HeadlessException {
super(title);
initialize();
}

/**
* @param args
*/
public static void main(String[] args) {
new Fenster("JList Demo").setVisible(true);
}

}

/**
* Änderbare Liste.
*
* @author Harald R. Haberstroh (hp)
*
*/
class List extends JList {
List() {
super(new DefaultListModel());
}

DefaultListModel getContents() {
return (DefaultListModel) getModel();
}

}

Labels: ,


Montag, 26. Januar 2009

 

GUI mit Netbeans erstellen und mit Eclipse weiterbearbeiten

Ich kenne keinen brauchbaren GUI-Designer für eclipse. Der GUI-Designer von Netbeans funktioniert ganz gut, dennoch verwende ich sonst zum Arbeiten lieber eclipse. eclipse compiliert immer im Hintergrund und ist daher in der Bedienung schneller. Außerdem hat es das Scrap Book, wo man mal schnell ein paar Zeilen probieren kann ohne gleich eine ganz Klasse schreiben zu müssen. Aber das meiste ist einfach Geschmacksache.

Netbeans verwendet eine eigene Klassen-Library für die graphischen Oberflächen. Die muss man auch im eclipse-Projekt verwenden. Es sind das die Libraries appframework-1.0.3.jar und swing-worker-1.1.jar, zu finden im Verzeichnis java2/modules/ext/ unterhalb des Netbeans-Installationsverzeichnisses (bei mir /usr/local/netbeans-6.1/).

Also zunächst ein "Java Desktop Application"-Projekt anlegen, das GUI erstellen und den About-Dialog ändern.

Als nächstes legt man ein einfaches Java-Projekt in eclipse an und kopiert die Pakete (Klassendateien) in das entprechende Projektverzeichnis, z.B.
hh@turing:~/netbeans/Gui2Eclipse/src> ls gui2eclipse/
Gui2EclipseAboutBox.form  Gui2EclipseAboutBox.java  Gui2EclipseApp.java  Gui2EclipseView.form  Gui2EclipseView.java  resources
hh@turing:~/netbeans/Gui2Eclipse/src> cp -r gui2eclipse/ ~/workspace/Gui2Eclipse/src/
hh@turing:~/netbeans/Gui2Eclipse/src> cd ~/workspace/Gui2Eclipse/
In eclipse muss man natürlich ein "refresh" machen. Dann erscheinen die Pakete mit den Klassen im Package Explorer, allerdings mit Fehlern, da ja die Libraries fehlen. Um dieses Problem zu beheben, legen wir im Quell-Ordner des Projekts (src/) ein lib/-Verzeichnis an, in die wir die nötigen Libraries kopieren:
hh@turing:~/workspace/Gui2Eclipse/src> mkdir lib
hh@turing:~/workspace/Gui2Eclipse/src> cd lib
hh@turing:~/workspace/Gui2Eclipse/src/lib> cp /usr/local/netbeans-6.1/java2/modules/ext/appframework-1.0.3.jar .
hh@turing:~/workspace/Gui2Eclipse/src/lib> cp /usr/local/netbeans-6.1/java2/modules/ext/swing-worker-1.1.jar .

Wo sich diese Jar-Dateien befinden ermittelt man aus dem "Properties"-Dialog von Netbeans:


Im eclipse muss man wieder ein "refresh" machen und anschließend die Libraries "aktivieren": Build Path->Add External Achives

Dass die Jar-Files eingebunden sind, sieht man im Projekt-Explorer:

Die Applikation lässt sich dann starten und ändern.

Aber Achtung: der von Netbeans erzeugte Code ist nicht so einfach. Man muss sich vorher schon ziemlich genau überlegen, wie man sein System gestaltet und dann erst das GUI dazu machen.

Labels: , , , ,


This page is powered by Blogger. Isn't yours?

Abonnieren Posts [Atom]