Sonntag, 29. März 2009
Robo*Stars qualifizerten sich für den RoboCup
Wir können stolz sein auf unser Robotik-Team Robo*Stars - Gabriel Grill, Stefan Walker, Daniel Grübler und Bianca Deinhammer haben den 4. Platz im RoboCupJunior Austria Open im Bewerb Dance Secondary geschafft. Damit haben sie sich für den internationalen RoboCup in Graz qualifiziert.
Der Wettbewerb fand vom 28.3. bis zum 29.3.2009 in der Fachhochschule Technikum Wien statt. Leider konnte Frau Deinhammer gar nicht und Herr Grübler nur am Samstag teilnehmen.
Ich möchte dem Team ganz herzlich zu der großartigen Leistung gratulieren, insbesonders den Herren Gabriel Grill und Stefan Walker, die als einzige diesen Bewerb nur zu zweit bestritten.
Hier das Video des "Auftritts" am Sonntag:
Bericht in Futurezone.
Eindrücke zum Bewerb gibt's hier:
Labels: allgemeines, C, Java, RoboCup, Robotik
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
Verkettete Listen
null
.In Java könnte das etwa so formuliert werden:
class Element {
Element next;
int data;
Element(int data) {
this.data = data;
}
}
Element anker = null; // leere Liste
Element n = new Element(1);
n.next = anker;
anker = n;
n = new Element(2);
n.next = anker;
anker = n;
n = new Element(3);
n.next = anker;
anker = n;
System.out.println("liste");
for (Element e = anker; e != null; e = e.next) {
System.out.println(e.data);
}
// 1. Element entfernen
anker = anker.next;
System.out.println("liste");
for (Element e = anker; e != null; e = e.next) {
System.out.println(e.data);
}
Folgendes Listing zeigt eine mögliche Implementierung einer einfach verketteten Liste sowie die Verwendung einer solchen Liste als Stack. Neue Elemente werden einfach vorne eingefügt:
public class Liste {
private class Element {
Element next;
int data;
Element(int data) {
this.data = data;
}
}
private Element anker = null;
// private Element ende = null;
public void append(int data) {
if (anker == null) {
anker = new Element(data);
} else {
// suche Ende
Element e = anker;
while (e.next != null) {
e = e.next;
}
// e ist das letzte Element (Ende)
e.next = new Element(data);
}
}
public void insertFirst(int data) {
Element n = new Element(data);
n.next = anker;
anker = n;
}
public void deleteFirst() {
assert (anker != null);
anker = anker.next;
}
public void printList() {
System.out.print("[");
for (Element e = anker; e != null; e = e.next) {
System.out.print(e.data + " ");
}
System.out.println("]");
}
public void push(int data) {
insertFirst(data);
}
public int pop() {
assert (anker != null);
Element e = anker;
anker = anker.next;
return e.data;
}
public boolean isEmpty() {
return anker == null;
}
/**
* @param args
*/
public static void main(String[] args) {
Liste liste = new Liste();
liste.printList();
// liste.insertFirst(1);
// liste.insertFirst(2);
// liste.insertFirst(3);
liste.append(1);
liste.append(2);
liste.append(3);
liste.printList();
liste.deleteFirst();
liste.printList();
Liste stack = new Liste();
stack.push(23);
stack.push(3);
while (!stack.isEmpty()) {
System.out.println(stack.pop());
}
}
}
UrlyBird - Hinweis zu Synchronisierung
synchronized
schützen oder einen Wrapper aus java.utils.Collections verwenden. Hier die Verwendung mit TreeMap:
/**
* handle locks (lockcookies)
*/
private SortedMap<Long, Integer> locks;
//...
locks = Collections.synchronizedSortedMap(new TreeMap<Long, Integer>());
Durch den Aufruf
Collections.synchronizedSortedMap(new TreeMap<Long, Integer>())
wird jeder Zugriff auf die locks
synchronisiert.Donnerstag, 19. März 2009
Kleines Java-Quiz
Ich werde die Liste im Laufe der Zeit vervollständigen.
Viel Spaß!
Mittwoch, 18. März 2009
Suchen von Mustern mit Hilfe eines Automaten
Erstellen Sie ein Programm, welches Textdateien (auf Kommandozeile angegeben oder stdin
) liest und die gefundenen C Prototypen ausgibt. Das Programm soll aus (mindestens) zwei Klassen bestehen. Die Hauptklasse macht die Parameteranalyse und behandelt die Ein- und Ausgabe. Die zweite Klasse implementiert den Automaten (weitere Klassen bzw. enums nach Bedarf). Um andere Suchmuster zu bearbeiten, muss dann nur die Automatenklasse getauscht werden.
Die Automatenklasse soll einen Automaten implementieren, der die Prototypen (siehe unten) erkennt.
Arbeiten Sie in Zweiergruppen. Jede Klasse wird von einer Person implementiert und getestet.
Die einzeln getesteten Klassen sollen dann (nachdem der Testcode auskommentiert wurde) zum fertigen Programm verbunden werden.
Die Syntax der Prototypen darf vereinfacht werden (im Prinzip können auch Java-Methoden ohne Rumpf verwendet werden, jedoch ohne private
, public
etc.):
- nur einfache Typen (
int
,long
,datum_t
o.ä.) sind erlaubt - Zeiger können weggelassen werden (optionaler
*
darf entfallen) - parameterlose Prototypen haben nur
()
, nicht(void)
Beispiele gültiger Prototypen:
int getX();
long setVal ( long x , long y ) ;
char zeichen23(int i);
Typen und Namen sind Worte. Worte müssen mit einem Buchstaben oder Unterstrich (_
) beginnen und können dann mit beliebig vielen Buchstaben Ziffern oder Unterstrichen fortgesetzt werden.
Zwischen Worten muss mindestens ein Leerzeichen sein, vor und nach (
, )
, ,
und ;
dürfen beliebig viele Leerzeichen (auch keine) sein.
Erstellen Sie eine alternative Automatenklasse, welche (einfache) Variablendeklarationen erkennt.
Nennen Sie das Projekt klasse-automat-name
(klasse
...2ad, 2bd, 2cd, name
Ihr Name).
JavaScript steigt, Perl fällt
Interessant. Java und C führen weiterhin die Liste an. JavaScript wird in der Schlagzeile erwähnt, weil JavaScript noch nie so weit oben war. Bei Perl ist es genau umgekehrt.
Die 5-Jahresübersicht zeigt den "Absturz" von Perl ganz deutlich:
Labels: allgemeines, Programmiersprachen
einfache erkennende Automaten, implementiert mit Zustandstabelle
- Telefonnummern mit Vorwahlen (/) und Durchwahlen (-)
- e-Mail Adressen
- Postleitzahlen
- URLs, beschränken Sie sich aber auf das http-Protokoll. Es sollen auch keine cgi-Aufrufe geprüft werden (Parameter). Verwenden Sie der Einfachheit halber nur Kleinbuchstaben.
Die Automaten sollen jeweils eine Methode
implementieren, welche
boolean accept(String input)
true
liefert, wenn der Automat den String input
akzeptiert.einfache erkennende Automaten, implementiert mit switch
- Telefonnummern mit Vorwahlen (/) und Durchwahlen (-)
- e-Mail Adressen
- Postleitzahlen
- URLs, beschränken Sie sich aber auf das http-Protokoll. Es sollen auch keine cgi-Aufrufe geprüft werden (Parameter). Verwenden Sie der Einfachheit halber nur Kleinbuchstaben.
switch
-Statements.Die Automaten sollen jeweils eine Methode
implementieren, welche
boolean accept(String input)
true
liefert, wenn der Automat den String input
akzeptiert.Dienstag, 17. März 2009
Execptions aus Threads bearbeiten
/** Throws an exception 50% of the time */
class NaughtyThread implements Runnable {
public void run() {
try {
if (Math.random() > .5) {
throw new RuntimeException("badness "
+ Thread.currentThread().getName());
}
} finally {
System.out.println("ran " + Thread.currentThread().getName());
}
}
}
Würden Sie ihn einfach ganz normal starten, würde der startende Thread nichts von der Exception mitbekommen:
Thread[] threads = { new Thread(new NaughtyThread(), "bad thread 1"),
new Thread(new NaughtyThread(), "bad thread 2"),
new Thread(new NaughtyThread(), "bad thread 3"), };
for (Thread t : threads) {
t.start();
}
try {
for (Thread t : threads) {
t.join();
}
} catch (InterruptedException e) {
System.err.println("interrupted: " + e.getMessage());
}
Die Lösung ist die Verwendung eines Proxies, der die Exception auffängt und speichert und auf Wunsch einen Callback aufruft.
/**
* Catches exceptions thrown by a Runnable, so you can check/view them later
* and/or deal with them from some callback.
*/
class RunnableCatch implements Runnable {
/** Proxy we will run */
private final Runnable _proxy;
/** Callback, if any */
private final RunnableCallback _callback;
/** @guarded-by(this) */
private Exception _exception;
public RunnableCatch(final Runnable proxy) {
this(proxy, null);
}
public RunnableCatch(final Runnable proxy, RunnableCallback target) {
_proxy = proxy;
_callback = target;
}
public void run() {
try {
_proxy.run();
} catch (Exception e) {
synchronized (this) {
_exception = e;
}
if (_callback != null) {
_callback.handleException(_proxy, e);
}
}
}
/** @return any exception that occured, or NULL */
public synchronized Exception getException() {
return _exception;
}
}
Der erzeugende Thread muss im Falle eines Callbacks das Interface
RunnableCallback
implementieren:
/** Called when an exception occurs */
interface RunnableCallback {
void handleException(Runnable runnable, Exception exception);
}
Unser
NaughtyThread
kann nun auf folgende Arten aufgerufen/gestartet werden. safe1
(t1
) ist der Thread ohne Callback, safe2
(t2
) ist jener mit Callback:
final Runnable bad = new NaughtyThread();
// safe1 doesnt have a callback
final RunnableCatch safe1 = new RunnableCatch(bad);
// safe2 DOES have a callback
final RunnableCatch safe2 = new RunnableCatch(bad, new RunnableCallback() {
public void handleException(Runnable runnable, Exception exception) {
System.out.println("Callback handled: " + exception.getMessage());
exception.printStackTrace();
}
});
final Thread t1 = new Thread(safe1, "bad thread 1");
final Thread t2 = new Thread(safe2, "bad thread 2");
t1.start();
t2.start();
t1.join();
t2.join();
if (safe1.getException() != null) {
System.out.println("thread 1 finished with exceptions");
safe1.getException().printStackTrace();
}
System.out.println("done");
Quelle: What is the best approach to handling exceptions thrown in a separate thread? auf www.stackoverflow.com
Labels: Java
Testklasse für UrlyBird
db-1x2.db
im Basisverzeichnis des Projekts liegt, die Data-Klasse sowie die entsprechenden Exceptions im vorgegebenen Package liegen.Man kann die Testklasse als Programm starten:
java -cp ../vendor/junit.jar:. test.DataTest
Dabei ist zu beachten, dass man JUnit 3.8 im
CLASSPATH
hat (Parameter -cp ../vendor/junit.jar:.
für Linux, wenn junit.jar
im angegebeenen Verzeichnis ist). Mit der Option --gui
kann man die Tests auch mit einer graphischen Oberfläche starten:
java -cp ../vendor/junit.jar:. test.DataTest --gui
In Eclipse kann man die Klasse natürlich auch als JUnit-Test starten.
Die Testklasse legt bei jedem Test eine Kopie der Datenbank (
db-1x2.db.sav
) an, damit die Originaldatenbank erhalten bleibt. Nach jedem Test wird die Datenbank wieder hergestellt.Achtung: Die Testklasse geht davon aus, dass zusätzlich zum geforderten Interface zwei Methoden implementiert wurden:
getCntRecords()
... liefert die Anzahl der Datensätze (incl. jener, die als gelöscht markiert sind).getNumFields()
... liefert die Anzahl der Felder.
RecordNotFoundException
bewirkt, gelöschte Datensätze für neue verwendet werden sollen und es andererseits keinen EOF-Mechanismus gibt. Man könnte diesen Unit-Test diesbezüglich an die gegebene Datei anpassen und die Anzahl der Datensätze fix definieren.Die Anzahl der Felder ist eigentlich auch vorgegeben und könnte daher im Test fix definiert werden.
Wollen Sie sich genau an das geforderte Interface halten, so müssen Sie die Testklasse entsprechend anpassen.
Die Angabe ist diesbezüglich ungenau.
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
Freitag, 6. März 2009
Literaturverwaltung
Eintrag
erfasst werden:- autor -
String(100)
- titel -
String(100)
- erscheinungsjahr -
int
- verlag -
String(30)
- zeitschrift -
String(30)
- seiten -
String(10)
- link -
String(60)
String(100)
bedeutet, dass dafür maximal 100 (genau 100) Zeichen gespeichert werden soll. Sie können das so implementieren, dass kürzere Strings hinten mit Leerzeichen gefüllt werden. Längere Strings müssen abgeschnitten werden. Für eine eventuelle Anzeige/Ausgabe müssen aber die Füllzeichen entfernt werden. Die Klasse Eintrag
ist mit entsprechenden Getter- und Setter-Methoden auszustatten (die bei den Strings das Abschneiden und Auffüllen bewerkstelligen).Die Klasse
Literatur
für die Verwaltung der Datensätze soll folgende Methoden bereitstellen:public void neuerEintrag(Eintrag eintrag)
- fügt einen neuen Eintrag in die Datei ein.public Eintrag lese(int nummer)
- liefert den Eintrag an der Stellenummer
odernull
, falls dieser Eintrag nicht existiert.public void loescheEintrag(int nummer)
- löscht den Eintrag an der Stellenummer
(tut nichts, wenn es den Eintrag nicht gibt).
public Literatur(RandomAccessFile file)
Implementieren Sie das Löschen so, dass der jeweilige Eintrag als gelöscht markiert wird (zusätzliches Feld) und diese Änderung in der Datei gespeichert wird. Der Datensatz ist also physisch noch in der Datei. Bei der Methode
lese()
muss also im Falle eines gelöschten Eintrags null
geliefert werden.Umgekehrt muss bei
neuerEintrag()
zunächst ein gelöschter Eintrag gesucht werden. Wird ein solcher gefunden, so wird er durch den neuen ersetzt (Lösch-Markierung muss wieder zurückgesetzt werden). Wird kein gelöschter Datensatz gefunden, so ist der neue an die Datei anzuhängen.Schreiben Sie ein kleines Testprogramm zum Testen Ihrer Klassen.
Anmerkung: Die Daten sind für eine echte Anwendung nicht ausreichend und müssten unterteilt werden, da es z.B. mehrere Autoren für einen Artikel gibt.
Hinweis: hier finden Sie eine Testklasse zum Testen der Klasse
Literatur
. Die Klasse Literatur
muss wie diese Testklasse im Package literatur
sein. Weiters muss die Library für JUnit 3 angegeben werden. Diese Testklasse trifft gewissen Annahmen zur Implementierung von Literatur
und Eintrag
. Bitte passen Sie entweder die Testklasse an Ihre Implementierung an oder Sie ändern Ihre Literatur
bzw. Eintrag
-Klassen entsprechend (z.B. nimmt der Test an, dass es eine read()
und eine write()
-Methode gibt, die einen Datensatz ("in sich hinein") list bzw. einen Datensatz ("sich selbst") schreibt (jeweils an die Position, die in Literatur
bestimmt wurde).Nennen Sie das Projekt
klasse-literatur-name
(klasse
... 2ad, 2bd, 2cd, name
... Ihr Familienname), z.B. 2ad-literatur-haberstroh
.Mittwoch, 4. März 2009
CVS - "cvs commit: nothing known about ..."
cvs add file
). Der Schüler hatte die Datei schon gelöscht. Also ist auch kein cvs add
nötig. Aber aus einem mir (noch) unbekannten Grund versucht Netbeans doch ein "commit" auf diese (nicht existierende) Datei zu machen.Folgenden Workaround habe ich gefunden:
- Alle einzelnen Dateien des Projekts händisch commiten.
- Das Projekt in einem neuen Verzeichnis auschecken.
- Prüfen, ob alles da ist.
- Das alte/originale Projekt löschen.
- Das neue verwenden.
cvs add
kümmern. Da würde man verstehen, warum man eine nicht existierende Datei nicht "commiten" kann. Hier war aber die Datei offensichtlich nicht vorhanden und trotzdem versuchte Netbeans ein "commit".Grundsätzlich vereinfacht aber Netbeans (und auch eclipse) die Verwendung von CVS - schon alleine die umständlichen Schritte beim Anlegen (import) entfallen.
cvs add
braucht man nicht machen.Scheinbar ein Bug im Netbeans.
Links:
Labels: CVS, eclipse, Fehler, netbeans
Abonnieren Posts [Atom]