Montag, 6. Juni 2011
Listings - Source Code drucken
Labels: allgemeines, POS1-1, POS1-2, PR2, PR3, PR5
Mittwoch, 13. Januar 2010
Ringbuffer Simulation


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.
Mittwoch, 4. November 2009
Projektnamen im CVS für Abgaben
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: allgemeines, Aufgabe, PR2, PR3, PR5
Samstag, 16. Mai 2009
Einfaches GUI-Beispiel ohne GUI-Builder mit Grafik
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);
}
}
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.
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).
Speicherlayout
#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;
}
- Was bedeuten die Ausgaben des Programms bzw. welchen Zweck verfolgt dieses Programm?
- Was bedeuten die Argumente von
main?
Mittwoch, 6. Mai 2009
Einführungsbeispiel C
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.Donnerstag, 30. April 2009
Musterlösung zu Unit-Tests
- 0 Elemente aufnehmen, Anzahl muss gleich bleiben
- 0 Elemente löschen, Anzahl muss gleich bleiben
- negative Anzahl Elemente aufnehmen, Exception
- negative Anzahl Elemente löschen, Exception
- nicht vorhandenes Element löschen, Exception
- 10 Elemente aufnehmen, Anzahl testen
- 10 Elemente aufnehmen, Ergebnis des Iterators testen
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;
}
}
Mittwoch, 22. April 2009
Beispiel zu Unit-Tests
import java.util.Iterator;Denken Sie bei der Erstellung der Tests an Grenzfälle, Sonderfälle und erst dann an den "Normalfall".
/**
* @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();
}
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
Mittwoch, 25. März 2009
JTable
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: Aufgabe, Java, PR3, PR5
Donnerstag, 19. März 2009
Kleines Java-Quiz
Ich werde die Liste im Laufe der Zeit vervollständigen.
Viel Spaß!
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 aus, falls diese Property existiert.hallo
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)
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 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
Donnerstag, 26. Februar 2009
Array sortieren
sort() aus der Klasse java.util.Arrays sortieren, wenn die Objekte das Interface Comparable implementiert. Dieses Interface verlangt, dass eine Methodeimplementiert wird. Diese Methode wird verwendet, um Objekte miteinander zu vergleichen. Der Typ
public abstract int compareTo(Object other);
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.
Mittwoch, 25. Februar 2009
Killer Tux
Labels: allgemeines, Linux, PR2, PR3, PR5
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: C, Java, PR2, PR3, PR5
Donnerstag, 19. Februar 2009
Swing und AWT mischen
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.Mittwoch, 11. Februar 2009
einfaches JList-Beispiel
- muss man die JList in eine ScrollPane verpacken, damit man bei langen Listen Scrollen kann.
- muss man sich um das "ListModel" kümmern. Das ListModel dient zur Verwaltung von Daten in der Liste.

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();
}
}
Montag, 26. Januar 2009
GUI mit Netbeans erstellen und mit Eclipse weiterbearbeiten
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: eclipse, Java, netbeans, PR3, PR5
Abonnieren Posts [Atom]




