Installing debian “squeeze” with PXE boot on a Samsung N145 Plus netbook

Introduction

This article describes the steps necessary to install debian 6 “squeeze” on a Samsung N145 Plus netbook, with the following specification:

  • Intel Atom processor
  • 10.1″ display
  • 1GB RAM
  • 340GB HDD
  • Windows 7 preinstalled

Setting up netboot of the debian installer

DHCP requests in my home LAN network is provided by dnsmasq on a desktop PC running GNU/linux debian stable (which at the time of writing, was Debian 6 squeeze). One nice feature of dnsmasq is that it can provide PXE network boot.

So what I did was to download the i386 network boot image and put the contents in the /var/tftpd/debian-installer/i386 directory of the computer running dnsmasq, and then edit the /etc/dnsmasq.conf file in the following way:

  1. Remove the comment in front of the dhcp-boot config line:
    dhcp-boot=pxelinux.0
  2. Set the tftp-root pointing to the directory containing the pxelinux.0 file:
    tftp-root=/var/tftpd/debian-installer/i386

Installing debian

Booting from the network

I connected the netbook with to the switch in my home LAN an RJ45  twisted pair cable, and powered on the netbook, and kept the F12 button pressed during boot, and ended up in the debian text based installer.

I set the time zone and location of the install (Oslo, Norway), created an initial user and set the root password.

Partitioning

The netbook came with a 340GB and Windows 7 preinstalled.  The hard disk was partitioned so that the Win7 system had both a C: and a D: drive, with the operating system installed on the C: drive.

The plan was to keep the Windows 7 installation, sans its D: drive and install debian in the part of the hard disk occupied by the D: drive.

The initial partitioning table looked like this:

#1 primary 104.9 MB B ntfs
#2 primary 93.4 GB ntfs
#5 logical 138.3 GB ntfs
#4 primary 28.2 GB ntfs

I guessed that partititon #1 was the boot partition, and that partition #2 was the C: drive containing the Windows 7 installation, and that #4 was either some kind of Samsung software (diagnostics possibly) or something belonging to the Windows 7 installation.

I left partition #1, #2 and #4 alone, and deleted the partition containing the D: drive (partition #5), and turned that into free space:

#1 primær 104.9 MB B ntfs
#2 primær 93.4 GB ntfs
pri/log 138.3 GB FREE SPACE
#4 primær 18.2 GB ntfs

I added a swap partition twice the size of the physical memory i.e. 2GB, and added an ext3 partition using the rest of the free space, and ended up with a partitioning table looking like this:

#1 primary 104.9 MB B ntfs
#2 primary 93.4 GB ntfs
#5 logical 136.3 GB B f ext3 /
#6 logical 2.0 GB f swap swap
#4 primary 18.2 GB ntfs

I saved the partitioning table and continued.

Installing the system

After completing the partitioning, I selected the following items to install:

  • SSH server
  • Laptop
  • Base tools

I let the installer run, using defaults for all questions. I answered YES to the question of whether GRUB should be installed on MBR. The installer found the Windows 7 installation and added it to the GRUB boot menu.  When the time came to reboot, I let the installer reboot.

After the reboot I logged in as root and installed the “KDE Plasma netbook” package:

apt-get install plasma-netbook kde-l10n-nb

I opened the /etc/apt/sources.list in a text editor, and modified it:

I then updated the APT database with the new sources and added all updates to the already installed software:

apt-get update
apt-get install linuxmint-keyring
apt-get update
apt-get dist-upgrade

I then installed all software I assumed was necessary:

apt-get install ttf-mscorefonts-installer
apt-get install openoffice.org openoffice.org-l10n-nb
apt-get install firefox firefox-l10n-nb

I rebooted the laptop and then logged into the plasma desktop using the user created at the start of the installation process. The desktop was missing network support and other useful software.

I logged in as root using the “failsafe” alternative, and installed missing software in the terminal window:

apt-get install network-manager-kde update-notifier-kde
apt-get install synaptic software-center gdebi

I rebooted and logged into plasma again. I tried to plug in an USB flash memory, and discovered that the desktop had no file manager, konqueror was missing. I installed konqueror (and discovered I should have picked the package “kde-plasma-netbook”, rather than just “plasma-netbook”):

apt-get install konqueror

The plasma desktop looked great, but was way to slow on an atom processor without much in the way of graphical hardware acceleration.

So I decided to try gnome and installed gnome with the command:

apt-get install gnome

I let apt set gdm3 as the default login instead of kdm.

I rebooted and logged into the gnome desktop, and it performed a lot better than the plasma desktop.

I rebooted again chose Windows 7 from the grub menu, and Windows 7 booted and logging into the desktop worked.

Making the Fn keys adjust the display brightness

The Fn keys for the adjusting the brightness didn’t work. I googled, and found two promising web pages:

  1. Fixing brightnes control, etc. on a Samsung R510 with Debian Squeeze
  2. InstallingDebianOn Samsung Samsung N150

I decided to try the first approach, and downloaded the packages created for Ubuntu Natty from https://launchpad.net/~voria/+archive/ppa

I then installed the downloaded .deb packages in the following way:

  1. Installed the easy-slow-manager:
    1. I let gdebi pull in all depdendencies (gcc, the linux-headers, make, etc)
  2. Installed samsung-backlight:
    1. Edited /etc/default/grub changing the line GRUB_CMDLINE_LINUX_DEFAULT
      GRUB_CMDLINE_LINUX_DEFAULT="quiet"
      to
      GRUB_CMDLINE_LINUX_DEFAULT="quiet acpi_backlight=vendor"
    2. Ran the command
      update-grub
  3. Installed samsung-tools:
      1. Installed the devscripts
        apt-get install devscripts
      2. Unpacked the samsung-tools tarball
        cd /tmp
        tar zxvf samsung-tools_1.4~ppa3~loms~natty.tar.gz
        cd /tmp/samsung-tools_1.4~ppa3~loms~natty
        dch -l sb
        1. Added “Compiled for debian squeeze” as the final comment

     

     

  4. Built the deb package
    cd /tmp/samsung-tools-1.4~ppa3~loms~nattysb1
    dpkg-buildpackage -rfakeroot -us -uc
  5. Installed the deb package
    gdebi /tmp/samsung-tools_1.4~ppa3~loms~nattysb1_all.deb
    1. I let gdebi install all of the required dependencies
  6. Rebooted

After the reboot I tried the Fn+Up and Fn+Down keys to adjust the display brightness and the keys worked fine.

Objektorientering i Java: “Monstersim”

English summary: This article is in Norwegian and presents a step-by-step receipe for creating a Java program that simulates a dragon and a troll in a cave. The purpose is to give an introduction to the Java programming language, object orientation, computer simulation, and the eclipse Java IDE’s support for aiding coding (in particular “Quick Fix”).

Denne artikkelen tar deg gjennom en steg-for-steg bruksanvisning for å lage et program som simulerer en drage og et troll i ei grotte. Artikkelen er ganske lang, men bør være rett fram å følge.

Hensikten er å gi et innblikk i Java, objektorientering, objektorienterte simuleringer, og eclipses støtte for å skrive kode (spesielt “Quick Fix”).

Artikkelen nevner begrep som “klasse” og “type” og “objekt” og “felt” og “metode” og “konstruktør”, men vil ikke gjøre noe forsøk på å forklare dem. Google er fin og ty til om man lurer på noe her.

Artikkelen hinter om begrepet arv/supertyper (Actor), men gjør ikke noe forsøk på å forklare dem. Meningen er at man skal lære (og forstå) av å gjøre heller enn å høre.

Bittelitt om Java

Java, som er språket eclipse er skrevet i, er et objektorientert språk. Begrepet “objektorientert programmeringsspråk” kommer fra Universitetet i Oslo, på 1960-tallet og fra et språk som heter Simula.

Som navnet “Simula” antyder, så var språket beregnet på å gjøre datasimuleringer. Ideen var at man skulle beskrive ting i den virkelige verden med objekter i datamaskinen, og at objektene skulle ha sin egen kode som beskrev oppførsel og hvordan de reagerte mot andre objekt.

Hva som skal simuleres

I simuleringa skal vi la en drage og et troll vandre fra rom til rom i ei grotte.

Om dragen og trollet møtes i samme rom, vil de slåss. Normalt vil trollet vinne over dragen.

Men det vil også finnes en gullskatt i et av rommene, og om dragen kommer over gullskatten før den treffer trollet, så vil dragen bli i det rommet og beskytte gullskatten. Gullskatten vil også gi dragen styrke sånn at den klarer å vinne over trollet, om trollet skulle virre inn i rommet til dragen.

Prosjekt, program og simulator

Start eclipse og i “Select a workspace”-dialogen, angi

C:userssteinarbworkspacesmonstersim

(bytt ut “steinarb” med navnet på din egen bruker)

Velg du fra menyen

File -> Java Project

I “Project name:” skriver du

monstersim

og klikk på “Finish”-knappen, og klikk på teksten “Workbench” i vinduet under.

Høyreklikk så på “monstersim” i “Package Explorer” (til venstre i Eclipse) og velg “Package”. I Name-ruta, gi teksten:

no.example.monstersim

Høyreklikk så på teksten “no.example.monstersim”, og velg

New -> Class

I “Name:” skriver du:

Monstersim

Klikk også i sjekkboksen ved teksten “public static void main(String[] args”, og klikk så på “Finish”-knappen.

I Monstersim.java som dukker opp i Eclipse, endre main()-metoden, til:

public static void main(String[] args) {
        int numberOfSimulations = 10;
        int caveWidth = 3;
        int caveHeight = 3;
        Simulator simulator = new Simulator(caveWidth, caveHeight);

        simulator.simulate(numberOfSimulations);
}

I venstre marg på Monstersim.java, så vises et ikon som består av ei lyspære, delvis overlappet av en rød firkant med en hvit “X” i seg.

Når du klikker på dette ikonet vil meny (“quick fix”-menyen) åpne seg. Dobbeltklikk på valget

Create class "Simulator"

I “Java Class”-dialogen, klikk på “Finish”-knappen. En ny tab med “Simulator.java” vil åpne seg.

Nede i eclipse er det en tab som heter “Problems”. Velg “Problems. I “Problems”, er det to linjer som starter med samme ikon som det du klikket på. Høyreklikk på linja som sier

The constructor Simulator(int, int) is undefined

og velg “Quick Fix”.

Velg

Create constructor "Simulator(int, int)"

og klikk på “Finish”-knappen. Eclipe bytter til Simulator.java igjen. Bytt ut TODO-linja i “Simulator(int, int)” sånn at konstruktøren blir seende sånn ut:

public Simulator(int caveWidth, int caveHeight) {
        this.caveWidth = caveWidth;
        this.caveHeight = caveHeight;
}

Set cursoren på “this.caveWidth” i første linje, og trykk “Ctrl-1” (hold nede “Ctrl”-tasten mens du trykker og slipper “1”). Dette aktiverer vår venn “Quick Fix”. Velg

Create field 'caveWidth'

Trykk Ctrl-. (hold nede Ctrl og trykk og slipp punktum-tasten) for å gå til neste feil, og trykk Ctrl-1 og bruk “Quick Fix” for å lage et felt for “caveHeight” også.

Velg fra toppmenyen

File -> Save All

I Problems-tabben står ei linje med teksten “The method simulate(int) is undefined for the type Simulator” (det er også to Warnings, men dem bryr vi oss ikke om nå). Høyreklikk på linja, velg “Quick Fix” og i “Quick Fix”-dialogen, velg linja

Create method 'simulate(int)' in type 'Simulator'

og klikk på “Finish”-knappen.

Velg nok en gang fra toppmenyen

File -> Save All

Lage simulator-“motoren”

Endre Simulator.simulate()-metoden sånn at den ser sånn ut:

public void simulate(int numberOfSimulations) {
        System.out.println("Simulating a " + caveWidth + "x" + caveHeight + " cave:");
        Cave cave = new Cave(caveWidth, caveHeight);
        List<Actor> actors = setupActors();
        cave.addActorsToRooms(actors);
        cave.printDescriptions();
        for (int simulationStepNumber=0; simulationStepNumber<numberOfSimulations; ++simulationStepNumber) {
                Actor deadActor = oneStep(simulationStepNumber, actors);
                if (null != deadActor) {
                        System.out.println(deadActor.getName() + " is dead.  Ending simulation!");
                        return ;
                }
        }
}

Sett kursoren på “Cave” i “Cave cave”, og så Ctrl-1, og velg

Create class 'Cave'

Ta Ctrl-Shift-s for å lagre alle filer.

Ta Ctrl-F6 for å gå til “Simulator.java”. Ta Ctrl-. for å gå til neste feil, og så “Quick Fix” og velg

Create constructor 'Cave(int, int)'

Trykk RET to ganger.

Ta Ctrl-Shift-s for å lagre alle filer.

Ta Ctrl-F6 for å gå til “Simulator.java”. Ta Ctrl-. for å gå til neste feil, og så “Quick Fix” og velg

Import 'List' (java.util)

Ny Ctrl-., og så ny “Quick Fix” og velg

Create class 'Actor'

klikk på Finish.

Ta Ctrl-Shift-s for å lagre alle filer, og så Ctrl-F6 for å gå tilbake til Simulator.java.

Gå til feilen ved “setupActors()” og ny “Quick Fix” og velg

Create method 'setupActors()'

Gå til feilen ved “addActorsToRoom” og ta “Quick Fix” og velg

Create method 'addActorsToRoom(List<Actor>)' in type 'Cave'

Trykk RET to ganger for å velge default-verdier, og så lagre alle filer.

Gå tilbake til Simulator.java og velg feilen ved “printDescription” og ta “Quick Fix” og velg

Create method 'printDescriptions() in type 'Cave'

trykk RET to ganger, og så Ctrl-Shift-s for å lagre alle filer.

Gå til feilen ved “oneStep”, ta “Quick Fix” og velg

Create method 'oneStep(int, List<Actor>)'

Gå til feilen ved “getName”, ta “Quick Fix” og velg

Create method 'getName()' in type 'Actor'

trykk RET to ganger svar for å velge default-verdier og lagre alle filer, med Ctrl-Shift-s.

Bruk Ctrl-F6 til å bytte tilbake til Simulator.java og endre “oneStep” sånn at metoden blir seende sånn ut:

private Actor oneStep(int simulationStepNumber, List<Actor> actors) {
        System.out.println("Simulation step: "+(simulationStepNumber+1));
        moveTheActors(actors);
        letActorsAct(actors);
        return checkForDeadActors(actors);
}

Velg feilen i “checkForDeadActors”.

Bruk så “Quick Fix” (først Ctrl-1, så Ctrl-, til å gå til forrige feil, så Ctrl-1 for å trigge “Quick Fix”, så Ctrl-, for å gå til forrige feil osv.) til å lage de tre manglende metodene. Grunnen til å ta “Quick Fix” baklengs, er for å få de nylagde metodene i logisk rekkefølge i fila. “Quick Fix” lager alltid nye metoder rett under den metoden du står i når du kjører “Quick Fix”.

Bytt ut TODO i “moveTheActors”:

private void moveTheActors(List<Actor> actors) {
        for (Actor actor : actors) {
                actor.move();
        }
}

(om du bruker kopiering og liming herfra, vil innrykket på siste klammeparantes være feil)

Ta “Quick Fix” på feilen i “move” og velg

Create method 'move()' in type 'Actor'

trykk RET to ganger for å velge default, og lagre alle filer. Ta Ctrl-F6 for å gå tilbake til Simulator.java.

Ta Ctrl-Shift-f (hold nede Ctrl og Shift, mens du trykker og slipper f-tasten). Det vil reformatere Simulator.java og sørge for at innrykket på siste klammeparantes blir riktig.

Bytt ut TODO i “letActorsAct”:

private void letActorsAct(List<Actor> actors) {
        for (Actor actor : actors) {
                actor.act();
        }
}

Ta “Quick Fix” på feilen i “move” og velg

Create method 'act()' in type 'Actor'

trykk RET to ganger for å velge default, og lagre alle filer. Ta Ctrl-F6 for å gå tilbake til Simulator.java.

Om innrykket på nest siste klammeparantes er feil, ta en ny Ctrl-Shift-f.

Bytt ut TODO i “checkForDeadActors”:

private Actor checkForDeadActors(List<Actor> actors) {
        for (Actor actor : actors) {
                if (actor.isDead()) {
                        return actor;
                }
        }

        return null;
}

Ta “Quick Fix” på “isDead” og velg

Create method 'isDead()' in type 'Actor'

trykk RET to ganger for å velge default, og lagre alle filer. Ta Ctrl-F6 for å gå tilbake til Simulator.java.

Om innrykket på nest siste klammeparantes er feil, ta en ny Ctrl-Shift-f.

“Motoren” i simuleringen består av løkker. Ytterst er en løkke som teller gjennom alle stegene i simuleringen. For hvert steg går man gjennom løkker som animerer alle deltagerene i simuleringen.

Bygge grotta

Gå til Cave.java og bytt ut TODO i konstruktøren “Cave(caveWidth, caveHeight)” med:

public Cave(int caveWidth, int caveHeight) {
        this.caveWidth = caveWidth;
        this.caveHeight = caveHeight;
        createCave();
        connectRooms();
}

Sett kursoren på “caveWidth” i “this.caveWidth” og ta “Quick Fix”, og velg:

Create field 'caveWidth' in type 'Cave'

Gjør det samme for “caveHeight”

La “Quick Fix” lage metodene “createCave” og “connectRooms” og lagre alle filer.

Merk: “Outline”-vinduet til høyre i Eclipse viser metodene etterhvert som du lager dem. Ved å klikke på metodene der kan du navigere rett til metoden. Du kan også endre rekkefølgen på metodene med å dra og slippe dem i Outline-vinduet.

Merk2: På høyre side av Cave.java-vinduet vises blå firkanter for alle linjene som inneholder TODO. Ved å klikke på en blå firkant kan du navigere rett til TODO-linja.

Bytt ut TODO i “createCave” med dette:

private void createCave() {
        rooms = new Room[caveWidth][caveHeight];
        for (int xPos=0; xPos<caveWidth; ++xPos)
        {
                for (int yPos=0; yPos<caveHeight; ++yPos)
                {
                        rooms[xPos][yPos] = new Room(xPos, yPos);
                }
        }
}

Sett kursoren over “Room” i første linje og la “Quick Fix” lage klassen “Room”. Lagre alle filer og bruk Ctrl-F6 til å gå tilbake til Cave.java.

Sett kursoren over “rooms” i første linje og la “Quick Fix” lage feltet rooms, som skal se slik ut når det er ferdig:

private Room[][] rooms;

La “Quick Fix” lage konstruktøren “Room(int, int)”, og endre metoden til dette:

public Room(int xPos, int yPos) {
        super();
        name = "Room at (" + xPos + "," + yPos + ")";
}

La “Quick Fix” lage feltet “name”, og lagre alle filer.

Ta Ctrl-F6 for å gå tilbake til Cave.java. Bytt så ut innholdet i “connectRooms” med dette:

private void connectRooms() {
        for (int xPos=0; xPos<caveWidth; ++xPos)
        {
                for (int yPos=0; yPos<caveHeight; ++yPos)
                {
                        Room room = rooms[xPos][yPos];

                        if (xPos>0)
                        {
                                Direction direction = Direction.WEST;
                                Room neighbour = rooms[xPos-1][yPos];
                                room.setNeighbour(direction, neighbour);
                        }

                        if (xPos<caveWidth-1)
                        {
                                room.setNeighbour(Direction.NORTH, rooms[xPos+1][yPos]);
                        }

                        if (yPos>0)
                        {
                                room.setNeighbour(Direction.EAST, rooms[xPos][yPos-1]);
                        }

                        if (yPos<caveHeight-1)
                        {
                                room.setNeighbour(Direction.SOUTH, rooms[xPos][yPos+1]);
                        }
                }
        }
}

Sett kursoren på første feil i “Direction direction”, ta “Quick Fix” og velg:

Create enum 'Direction'

og klikk på “Finish”-knappen (eller bare trykk RET), lagre alle filer.

Ta Ctrl-F6 for å komme tilbake til Cave.java. Ta Ctrl-. for å gå til “WEST” og Ctrl-1 for å velge “Quick Fix” og velg så

Create enum constant 'WEST' in 'Direction'

Lagre alle filer og ta Ctrl-F6 for å komme tilbake til Cave.java og ta “Quick Fix” på “setNeighbour” og velg

Create method 'setNeighbour(Direction, Room)' in type 'Room'

og trykk RET to ganger for å velge default-verdier. Endre metoden til dette:

public void setNeighbour(Direction direction, Room neighbour)
{
        neighbours.put(direction, neighbour);
}

Gjør så en “Quick Fix” på “neighbours” og lag et felt. Endre feltet så det blir seende sånn ut (det blir default av type Object):

Map<Direction, Room> neighbours = new EnumMap<Direction, Room>(Direction.class);

Lagre alle filer og sett kursoren på “Map”, ta “Quick Fix”, og velg:

Import 'Map' (java.util)

Ta Ctrl-. og så Ctrl-1 og velg:

Import 'EnumMap' (java.util)

og lagre så alle filer.

Lagre alle filer og ta Ctrl-F6 for å komme tilbake til Cave.java og bruk så Ctrl-. og Ctrl-1 for å lage de resterende enum-konstantene. Lagre alle filer når du er ferdig, og gå til Cave.java.

Gå til “printDescriptions”, og endre TODO sånn at metoden blir seende sånn ut:

void printDescriptions() {
        for (int xPos=0; xPos<caveWidth; ++xPos)
        {
                for (int yPos=0; yPos<caveHeight; ++yPos)
                {
                        System.out.print(rooms[xPos][yPos].describe());
                }
        }
}

Gjør “Quick Fix” på linja med “describe()” og velg

Create method 'describe()' in type 'Room'

trykk RET en gang for å velge default, og skriv så “strin” (uten anførselstegn), skriv Ctrl-SPC og trykk RET.

Endre innhold i den nylagde metoden sånn at den blir seende sånn ut:

public String describe()
{
        if (contents.size()>0)
        {
                StringBuffer sb = new StringBuffer(name);
                sb.append(" contains:n");
                for (Actor actor : contents)
                {
                        sb.append(" ");
                        sb.append(actor.describe());
                        sb.append("n");
                }

                return sb.toString();
        }

        return "";
}

Gjør “Quick Fix” på feilen i “describe” og velg

Create field 'contents'

Trykk F3 for å gå til “contents” og endre feltet fra en Map til et Set:

Set<Actor> contents = new HashSet<Actor>();

“Quick Fix” feilen i Set og velg:

Import 'Set' (java.util)

“Quick Fix” feilen i HashSet og velg:

Import 'HashSet' (java.util)

Lagre alle filer med Ctrl-Shift-s.

Trykk Ctrl-. for å gå til feilen i “describe” og bruk “Quick Fix” og velg

Create method 'describe()' in type 'Actor'

trykk RET en gang for å velge default, og skriv så “strin” (uten anførselstegn), skriv Ctrl-SPC og trykk RET.

Endre “describe()” fra dette:

public String describe() {
        // TODO Auto-generated method stub
        return null;
}

til dette

public abstract String describe();

Lagre alle filer.

Ta “Quick Fix” på “describe”, og velg:

Make type 'Actor' abstract

Lagre alle filer og bruk Ctrl-F6 til å gå til Cave.java.

Sett kursoren på “name” og gjør “Quick Fix”, og velg:

Create getter and setter for 'name'

I dialogen “Encapsulate Field”, trykk på “OK”-knappen.

Lagre alle filer.

Gå til Cave.java, finn metoden “addActorsToRooms” og endre den til å se sånn ut:

void addActorsToRooms(List<Actor> actors) {
        for (Actor actor : actors) {
                rooms[randX()][randY()].enter(actor);
        }
}

Bruk “Quick Fix” for å lage metoden “randX” og endre den til å se sånn ut:

private int randX() {
        return randomGenerator.nextInt(caveWidth);
}

Bruk “Quick Fix” til å lage feltet “randomGenerator”, trykk F3 for å navigere til feltet og endre det fra

private Object randomGenerator;

til

private Random randomGenerator = new Random();

Bruk “Quick Fix” på “Random” og velg

Import 'Random' (java.util)

Gå til feilen med Ctrl-. og bruk “Quick Fix” til å lage metoden “randY”, som så endres til:

private int randY() {
        return randomGenerator.nextInt(caveHeight);
}

Sett kursoren i “enter”, bruk “Quick Fix” til å lage metoden i Room, og endre metoden “enter” sånn at den blir seende sånn ut:

public void enter(Actor actor) {
        contents.add(actor);
        actor.setCurrentLocation(this);
}

Lagre alle filer.

Gjør “Quick Fix” på “setCurrentLocation” og velg:

Create method 'setCurrentLocation(Room)' in type 'Actor'

trykk RET to ganger for å velge default

Endre innholdet i “setCurrentLocation” sånn at den blir seende sånn ut:

public void setCurrentLocation(Room room) {
        currentLocation = room;
}

Bruk “Quick Fix” til å lage feltet “currentLocation”.

Bygge deltagerene i simuleringen

Dette er objektene som blir animert av Simulator.

Actor og Monster

Gå til Actor.java (Ctrl-F6 er din venn).

Endre “getName()” til dette:

public String getName() {
        return name;
}

Bruk “Quick Fix” til å lage feltet “name”.

Endre “move” og “act” til dette:

public void move() {}
public void act() {}

Endre “isDead” til dette:

public boolean isDead() {
        return dead;
}

Bruk “Quick Fix” til å lage feltet “dead”.

Velg fra menyen:

Source -> Generate Getters and Setters...

I dialogen “Generate Getters and Setters”, klikk på “Select All” og klikk så på “OK”-knappen. Lagre alle filer.

Merk: om du ikke liker rekkefølgen på metodene så er det lett å sjonglere på rekkefølgen med å klikke og dra i dem i Outline-vinduet.

Så er det tid for monstrene.

I “Package Explorer”, høyreklikk på “no.example.monstersim” og velg:

New -> Class

I “New Java Class”-dialogen:

  • I Name: skriv
    Monster
    
  • Kryss av for “abstract”
  • I SuperClass: skriv
    Actor
    
  • Fjern avkryssing for “Inherited abstract methods”

Klikk på “Finish”-knappen

Velg fra menyen

Source -> Override/Implement Methods...

I dialogen “Override/Implement Methods”:

  • Fjern avkryssing for “describe”
  • Kryss av for “act”
  • Kryss av for “move”

Klikk på “OK”-knappen

Endre “move” til å se sånn ut:

@Override
public void move() {
        if (isDead()) {
                return;
        }

        Room thisRoom = getCurrentLocation();
        Direction direction = randomDirection();
        Room nextRoom = thisRoom.getNeighbour(direction);
        while (null == nextRoom) {
                nextRoom = thisRoom.getNeighbour(randomDirection());
        }

        thisRoom.leave((Actor)this);
        nextRoom.enter(this);
        System.out.println(" " + getName() + " moves from " + thisRoom.getName() + " to " + nextRoom.getName());
}

Gjør “Quick Fix” på “randomDirection” og lag en ny metode, som du endrer til å se sånn ut (merk, du må endre returtypen til Direction):

Direction randomDirection() {
        double number = Math.random();
        if (number < 0.25) {
                return Direction.WEST;
        } else if (number < 0.5) {
                return Direction.NORTH;
        } else if (number < 0.75) {
                return Direction.EAST;
        }

        return Direction.SOUTH;
}

Gjør “Quick Fix” på “getNeighbour” og velg

Create method 'getNeighbour(Direction)' in type 'Room'

trykk RET to ganger.

Endre “getNeighbour” til å se sånn ut:

public Room getNeighbour(Direction direction)
{
        return neighbours.get(direction);
}

Lagre alle filer og gå tilbake til Monster.java med Ctrl-F6.

Gjør “Quick Fix” på “thisRoom.leave” og velg

Create method 'leave(Monster)' in type 'Room'

Trykk RET to ganger. Endre metoden “leave” til:

public void leave(Actor actor) {
        actor.setCurrentLocation(null);
        contents.remove(actor);
}

Lagre alle filer og gå tilbake til Monster.java.

Endre “act” til å se sånn ut:

@Override
public void act() {
        if (isDead()) {
                return;
        }

        Set<Actor> roomContents = getRoomContentsExceptForMe();
        look(roomContents);
        take(roomContents);
        fight(roomContents);
}

Merk: om “act()” har fått feil innrykk, så bare merk hele metoden og ta Ctrl-i for å fikse innrykket.

(grunnen til at ting kan få feil innrykk er om de har feil i seg. I dette tilfelle er det Set og metoden getRoomContentsExceptForMe, som ikke er definert)

Sett kursoren på linja med “Set” og ta “Quick Fix” og velg

Import 'Set' (java.util)

Ny “Quick Fix”, og velg

Create method 'getRoomContentsExceptForMe'

Trykk F3 for å gå til den nye metoden og endre den til å se sånn ut:

private Set<Actor> getRoomContentsExceptForMe() {
        Set<Actor> roomContents = getCurrentLocation().getContents();
        roomContents.remove(this);
        return roomContents;
}

Ta “Quick Fix” på “getContents” og velg

Create method "getContents()" in type 'Room'

og trykk RET to ganger for å velge default-verdier.

Endre så metoden til å se sånn ut:

public Set<Actor> getContents() {
        return new HashSet<Actor>(contents);
}

Lagre alle filer og returner til Monster.java.

Sett kursoren på feilen i linja med “fight”, og bruk Ctrl-1 og velg

Create method 'fight(Set<Actor>)

og gå baklengs gjennom feilene med Ctrl-, og gjør “Quick fix” og lag metodene “take” og “look”.

Endre metoden “look”, til:

public void look(Set<Actor> roomContents)
{
        if (roomContents.size() > 0) {
                System.out.println(" " + getName() + " sees the following actors: ");
                for (Actor actor : roomContents) {
                        System.out.println("  " + actor.describe());
                }
        }
}

Ta Ctrl-Shift-f for å fikse innrykket til nest siste parantes (om du har kopiert og limt inn innholdet fra eksempelet).

Endre “take” til dette:

public void take(Collection<Actor> roomContents)
{
        Actor thePicker = this;
        for (Actor actor : roomContents) {
                actor.pickedUpBy(thePicker);
        }
}

Kjør “Quick Fix” på feilen i “pickedUpBy” og velg

Create method 'pickedUpBy(Actor)' in type 'Actor'

trykk RET to ganger for å ta defaulter.

Endre methoden “pickedUpBy” sånn at det blir en tom metode, dvs. fra

public void pickedUpBy(Actor thePicker) {
        // TODO Auto-generated method stub

}

til

public void pickedUpBy(Actor thePicker) {}

Lagre alle filer og gå tilbake til Monster.java.

Gå til feilen i “fightMe” og ta “Quick Fix” og så

public void fight(Collection<Actor> roomContents)
{
        Actor opponent = this;
        for (Actor actor : roomContents) {
                actor.fightMe(opponent);
        }
}

Endre metoden “fightMe” sånn at det blir en tom metode, dvs. fra

public void fightMe(Actor opponent) {
        // TODO Auto-generated method stub

}

til

public void fightMe(Actor opponent) {}

Mens vi er i Actor.java, så legger vi inn noen metoder til, som vi får bruk for seinere:

public double strength() { return 0.0; }
public void takeMe(Actor pickedUpThing) {}

Lagre alle filer og gå til Monster.java. Velg fra menyen

Source -> Override/Implement Methods...

I dialogen “Override/Implement Methods”

  • Fjern avkryssing for “describe”
  • Kryss av “fightMe”

Klikk på “OK”-knappen.

Endre metoden til dette:

@Override
public void fightMe(Actor opponent) {
        System.out.println(" " + opponent.getName() + " fights " + getName() + "!");
        if (opponent.strength() > strength()) {
                setDead(true);
                System.out.println(" Arrrrgh! " + getName() + " dies!");
        }
}

Lagre alle filer.

Enter the Dragon

Nå er det omsider tid for det første ordenlige monsteret. I “Package Explorer”, høyreklikk på no.example.monstersim, og velg

New -> Class

I dialogen “New Java Class”

  • I Name: skriv
    Dragon
    
  • I Superclass: skriv
    Monster
    

Klikk på “OK”-knappen.

Endre “describe”-metoden til å se sånn ut:

@Override
public String describe() {
        return "A dragon with green shimmering scales";
}

Velg fra menyen

Source -> Generate Constructors from Superclass...

I dialogen “Generate Constructors from Superclass”, trykk på “OK”-knappen.

Endre den nylagde konstruktøren til å se sånn ut:

public Dragon() {
        super();
        setName("Dragon");
}

Velg fra menyen

Source -> Override/Implement Methods...

I dialogen “Override/Implement Methods”

  • Kryss av for “move()”
  • Lukk “Monster”
  • Åpne “Actor”
  • Kryss av for “strength()”
  • Kryss av for “takeMe(Actor)”

Klikk på “OK”-knappen.

Endre metoden “takeMe” til (merk: også endring av argumentnavnet fra “me” til “pickedUpThing”):

@Override
public void takeMe(Actor pickedUpThing) {
        if (null == gold) {
                gold = pickedUpThing;
                System.out.println(" " + getName() + " picked up " + pickedUpThing.getName());
        }
}

Bruk “Quick Fix” på “gold” i

gold = pickedUpThing

for å lage et felt med navn “gold” (om du gjør “Quick Fix” på den første feilen med “gold” så får feltet type Object istedenfor Actor).

Det denne metoden gjør er at om dragen ikke allerede har funnet gull, så vil den ta i mot gullet og holde på det i et felt.

Reformater fila med Ctrl-Shift-f for å fikse innrykket til nest siste klammeparantes. Lagre alle filer.

Endre metoden “strength” til:

@Override
public double strength() {
        double myStrength = 0.25;
        if (null == gold) {
                return myStrength;
        }

        return myStrength + gold.strength();
}

Denne metoden gir styrken til dragen. Uten gull så er styrken 0.25. Med gull så er styrken til dragen 0.25 pluss styrken til gullet.

Vi vil også gjøre en siste ting: vi vil at dragen skal slutte å flytte seg når den har funnet gull.

I Dragon, endre metoden “move”, til:

@Override
public void move() {
        // A dragon that has found gold stays with it.
        if (null == gold) {
                super.move();
        }
}

Tid for troll

Monster nummer to er litt av et troll.

I “Package Explorer”, høyreklikk på no.example.monstersim, og velg

New -> Class

I dialogen “New Java Class”

  • I Name: skriv
    Troll
    
  • I Superclass: skriv
    Monster
    

Klikk på “OK”-knappen.

Endre “describe”-metoden til å se sånn ut:

@Override
public String describe() {
        return "A warty and ugly troll";
}

Velg fra menyen

Source -> Generate Constructors from Superclass...

I dialogen “Generate Constructors from Superclass”, trykk på “OK”-knappen.

Endre den nylagde konstruktøren til å se sånn ut:

public Troll() {
        super();
        setName("Troll");
}

Velg fra menyen

Source -> Override/Implement Methods...

I dialogen “Override/Implement Methods”

  • Lukk “Monster”
  • Åpne “Actor”
  • Kryss av for “strength()”

Klikk på “OK”-knappen.

Endre metoden “strength” til dette:

@Override
public double strength() {
        return 0.4;
}

Gå for Gull

I “Package Explorer”, høyreklikk på no.example.monstersim, og velg

New -> Class

I dialogen “New Java Class”

  • I Name: skriv
    Gold
    
  • I Superclass: skriv
    Actor
    

Klikk på “OK”-knappen.

Endre “describe”-metoden til å se sånn ut:

@Override
public String describe() {
        return "A mound of gold";
}

Velg fra menyen

Source -> Generate Constructors from Superclass...

I dialogen “Generate Constructors from Superclass”, trykk på “OK”-knappen.

Endre den nylagde konstruktøren til å se sånn ut:

public Gold() {
        super();
        setName("Gold");
}

Velg fra menyen

Source -> Override/Implement Methods...

I dialogen “Override/Implement Methods”

  • Kryss av for “pickedUpBy()”
  • Kryss av for “strength()”

Klikk på “OK”-knappen.

Endre metoden “strength” til:

@Override
public double strength() {
        return 0.5;
}

Endre metoden “pickedUpBy” til:

@Override
public void pickedUpBy(Actor thePicker) {
        thePicker.takeMe(this);
}

Dragon kaller “pickedUpBy” på alle andre Actors i det rommet den befinner seg i. Om den kaller “pickedUpBy” på et Troll, så skjer ingenting fordi da blir den tomme metoden i Actor kalt.

Men om den kaller “pickedUpBy” på Gold, så blir metoden over kalt, og den vil da kalle “takeMe” i implementasjonen i Dragon.

Merk at ingen monstere implementerer “pickedUpBy” så de vil være upåvirket av kall til denne metoden (kallene vil gå til den tomme implementasjonen i Actor).

Tilsvarende implementerer ikke Gold “fightMe” og vil derfor være upåvirket av forsøk på å starte en slåsskamp.

Første kjøring

Nå gjenstår bare en kodebit. Gå til Simulator.java og finn metoden “setupActors” og endre den til dette:

private List<Actor> setupActors() {
        ArrayList<Actor> actors = new ArrayList<Actor>();
        actors.add(new Dragon());
        actors.add(new Gold());
        actors.add(new Troll());
        return actors;
}

Bruk “Quick Fix” på feilen i ArrayList og velg:

Import 'ArrayList' (java.util)

Lagre alle filer.

Så kan simuleringen kjøres. Bruk Ctrl-F6 til å gå til Monstersim.java. Kjør fra menyen:

Run -> Run As -> Java Application

(påfølgende kjøringer kan gjøres med Ctrl-F11)

Da vil en utskrift av en simulering komme i Console-vinduet i eclipse. Her er et eksempel på ei simulering:

Simulating a 3x3 cave:
Room at (0,2) contains:
 A dragon with green shimmering scales
Room at (1,1) contains:
 A warty and ugly troll
Room at (2,1) contains:
 A mound of gold
Simulation step: 1
 Dragon moves from Room at (0,2) to Room at (1,2)
 Troll moves from Room at (1,1) to Room at (1,2)
 Dragon sees the following actors: 
  A warty and ugly troll
 Dragon fights Troll!
 Troll sees the following actors: 
  A dragon with green shimmering scales
 Troll fights Dragon!
 Arrrrgh! Dragon dies!
Dragon is dead.  Ending simulation!

Merk: Dobbeltklikk på Console-tabben for å la Console fylle hele Eclipse-vinduet. Dobbelt-klikk på Console-tabben igjen, for å ta den ned til normal størrelse. Denne oppførselen gjelder alle vindu i Eclipse. Tastatursnarveien til denne kommandoen er Ctrl-m.

En oppsummering

Noen ting å merke seg:

  • Dette programmet ble skrevet fra toppen og nedover, fordi vi visste hvor vi skulle. Det er som regel ikke tilfelle. Som regel starter man med de objektene man vet skal være med (i dette tilfelle i første omgang Dragon, Troll og Gold, og kanskje Room), og så begynner man å lage metoder for det man vet de skal gjøre: flytte på seg, se seg om, ta opp gjenstander, slåss. Så leter man etter felles ting med gjenstandene og lager super-typer (Monster for både Dragon og Troll og Actor for typene til alle objektene som skal delta i simuleringen)
  • Mesteparten av koden er rammeverk. Det er først når man begynner på subtypene til Actor at det kommer ting som faktisk har med simuleringen å gjøre
  • Det er kanskje litt rart at gullet er en Actor, ettersom det ikke kan røre på seg? Forklaringen er at det er heller navnet Actor som ikke passer. Alle ting som skal delta i simuleringen bør ha en felles type. Men AThingThatParticipatesInSimulation virket litt langt…
  • Det er kanskje litt rart at Room ikke er en Actor, når Gold er det…? Svaret er at i en annen sammenheng og en annen simulering så kunne det vært det. Men i den modellen her så er rom posisjonen til objektet
  • Simuleringen er ikke så langt unna starten på et tekstbasert adventure-spill. Det som trengs er
    • at den ytterste løkka i simuleringsmotoren ikke går et endelig antall steg, men at den avbrytes, f.eks. når spilleren avslutter
    • å lage en Actor, som på Actor.act() vil presentere spilleren med en beskrivelse av rommet han befinner seg i, og et spørsmål om hva han vil gjøre (gå? Slåss? Plukke opp en gjenstand?)

Ting å prøve:

  • Øke størrelsen på grotta? 3×3 er litt lite. Jeg gjorde den så liten for at monstrene skulle ha større sannsynlighet for å støte på hverandre i løpet av 10 simuleringssteg, som er ca. en skjermside
  • En morsommere grotte med mer tilfeldige koblinger?
  • Trollet kan kanskje også finne ting som øker styrken? En klubbe, f.eks.?
  • Flere monstre?
  • Flere gjenstander? (isåfall blir protokollen for å finne gull mer komplisert)
  • En rikere interaksjonsprotokoll? Idag har man mulighet for å slåss (“fightMe”) og å plukke opp ting “pickedUpBy”/”takeMe”. Er det flere ting man ønsker å gjøre?
  • Mer intelligens i monstrene? Kanskje drager og troll kan lukte gull, og at dragene vil søke mot gull, mens trollene vil unnvike det (kan ikke være absolutt, men kanskje sannsynligheten for å gå i retning av gull kan økes, eller senkes?)
  • Bør “look” være en egen operasjon som kjøres av simulatoren, på linje med “move” og “act”? Det vil gjøre det enklere å lage mer intelligens i Actorene
  • Spørsmål: hvorfor kjøres “move” separat, og ikke som en del av “act”?

Debugging i eclipse

English summary: This article is in Norwegian and describes debugging of Java code in the Eclipse Java IDE.

Debugging (“avlusing”) I eclipse, betyr å stoppe et program som kjører og se hvilke verdier variabler har. Å debugge Hello.java som det var, er ikke spesielt interessant. Det er f.eks. ingen variabler å kikke på utenom “args”, som her ikke inneholder noe interessant.

Om du endrer programmet til noe sånt, blir det litt mer interessant (du kan bruke Ctrl-SPC for å ekspandere og skrive ting, eller du kan bare lime inn koden som den står i eksempelet):

package no.example.hello;

public class Hello {

        /**
         * @param args
         */
        public static void main(String[] args) {
                int numberOfTimesToPrintMessage = args.length;
                for (int i=0; i<numberOfTimesToPrintMessage; i++) {
                    System.out.println("Hello world!");
                }
        }
}

Linja med “for” betyr at programmet skal gå i ei løkke et visst antall ganger og gjøre det som står inne i løkka hver av gangene.

Om du nå prøver å kjøre programmet, så ser du at ingenting kommer ut i Console-vinduet. Det var merkelig! På tide å debugge!

Høyreklikk i margen på linja

System.out.println("Hello world!");

og velg “Toggle breakpoint”.

Velg så fra toppmenyen

Run -> Debug As -> Java Application

Programmet vil kjøre som før, og ingenting vil fortsatt skrives ut.

Sett et brekkpunkt i linja med

for (int i=0; i<numberOfTimesToPrintMessages; i++) {

og trykk på F11 for å debugge på nytt.

Du vil få opp en dialog, som heter “Confirm Perspective Switch”. Bare velg “Remember my decision”, klikk på “Yes”-knappen og fortsett.

Vinduene i eclipse vil rearrangere seg, men kildekoden til Hello.java vil fortsatt være synlig. I et vindu som heter “Variables” så vil du se at “numberOfTimesToPrintMessages” har verdien “0”. Aha!

Koden

for (int i=0; i<numberOfTimesToPrintMessages; i++) {

betyr: la variabelen i ha verdiene fra 0 og til numberOfTimesToPrintMessages – 1, og for hver ny verdi av i, utfør koden mellom “{” og “}”.

Variabelen numberOfTimesToPrintMessages er definert sånn:

int numberOfTimesToPrintMessage = args.length;

dvs. antall kommandolinjeargument som er gitt til programmet.

Trykk “F8” for å la programmet fortsette. Programmet vil avslutte uten at noe blir skrevet ut.

For å komme tilbake til oppsettet du bruke for å lage Hello.java, velg fra menyen:

Window -> Openm Perspective -> Java

I vinduet “Package Explorer”, høyreklikk på “Hello.java” og velg “Properties”.

Velg “Run/Debug Settings”, velg “Hello” og klikk på “Edit…”.

I “Edit Configuration”-dialogen, velg “Arguments”-tabben, og i “Program Arguments”, legg inn:

first second third fourth

Kjør så programmet igjen

Run -> Run

og observer at Console nå får fire utgaver av hello world.

Noen øvelser: debug og se hvordan variablene endrer seg. Du kan bruke F8 for å la programmet kjøre til neste brekkpunkt.

En ting du kan gjøre er å bruke verdiene fra argumentene i utskriftene og så endre argumentene i Properties og se hvordan det endrer kjøringen av programmet.

Installere EGit i eclipse

English summary: This article is in Norwegian and describes installation of the EGit git version control plugin, in the Eclipse Java IDE.

Et “versjonskontrollsystem” lar programmereren lagre unna noe som fungerer for å kunne gå tilbake til det om det nye man prøver på feiler.

Denne typen system kan også brukes til å dele kode mellom maskiner og mennesker. Systemet som følger med eclipse heter CVS, og har vært en tro tjener i mange år.

Men jeg, og mange med meg, bruker et nyere og smartere system som heter “git”. For å installere støtte for git i eclipse:

  • Start eclipse
  • Velg fra menyen på toppen av eclipse:
    Help -> Install New Software...
    
  • I “Install”-dialogen:
    • I “Work with: “, velg “–All Available Sites–”
    • I ruta der det står “type filter text”, skriv:
      egit
      
    • Etter litt tid vil treeviewet under ha:
      • Collaboration
        • Eclipse EGit
        • Eclipse EGit Mylyn GitHub Feature
        • Eclipse EGit Mylyn
    • Kryss av “Eclipse EGit” og klikk på “Next>”-knappen
    • Klikk på “Next>”-knappen en gang til
    • Velg “I accept the term of the license agreement” og klikk på “Finish”-knappen
  • En dialog vil vises som spør om du skal ta omstart på eclipse: ta omstart på eclipse

Etter at eclipse har startet på nytt vil git-støtten til eclipse være installert.

Hello world! i eclipse

English summary: This article is in Norwegian and describes first startup of the eclipse Java IDE, and writing and running an “Hello World” application inside eclipse.

Når du starter eclipse, så er det første som skjer at det kommer opp en dialog som sier “Select a workspace”. Det som foreslås, er hos meg:

C:userssteinarbworkspace

(istedenfor “steinarb” så vil du se en katalog for ditt eget brukernavn)

Bytt dette ut med:

C:userssteinarbworkspaceshello

Et “workspace” i eclipse, er en samling av prosjekter. Om du har gjort deg litt kjent med Visual Studio, så tilsvarer et “workspace” i eclipse, det som heter “solution” i VS.

Du kan godt ha mange urelaterte prosjekter i en solution, men jeg synes det fort blir rotete. Så det man gjør med endringen, er å lage et workspace som heter “hello”, som skal inneholde et prosjekt som heter “hello”.

Så velger du fra menyen

File -> Java Project

I “Project name:” skriver du

hello

og klikk på “Finish”-knappen.

Så kan du enten lukke “Welcome”-vinduet, eller klikke på teksten “Workbench”.

Høyreklikk så på “hello” i “Package Explorer” (til venstre i Eclipse) og velg “Package”. I Name-ruta, gi teksten:

no.example.hello

Teksten her kan være hva som helst, men det er vanlig å bruke internett-domenenavn i reversert i starten av teksten. Dette er pakkenavnet som gjør klassen din helt unik (mange kan lage en klasse som heter “Hello”, men forhåpentligvis er det bare en “no.example.hello.Hello”).

Høyreklikk så på teksten “no.example.hello”, og velg

New -> Class

I “Name:” skriver du:

Hello

Klikk også i sjekkboksen ved teksten “public static void main(String[] args”, og klikk så på “Finish”-knappen.

Det da vil komme et vindu som heter “Hello.java”, med følgende innhold:

package no.example.hello;

public class Hello {

        /**
         * @param args
         */
        public static void main(String[] args) {
                // TODO Auto-generated method stub

        }

}

Merk så linja med TODO-kommentaren, og skriv bokstaven S og trykk Ctrl-SPC (hold ned Ctrl-tasten mens du trykker og slipper space-bar). Da kommer det en drop-down-meny med “System – java.lang” som førstevalg, og allerede valgt. Trykk på RET.

Skriv så punktum, og bokstaven “o”. Drop-down-menyen kommer opp på “.”, og på “o”, så er “out: PrintStream – System” valgt. Trykk RET igjen.

Skriv “.” (uten anførselstegn) igjen. Drop-down-menyen dukker opp igjen. Skriv teksten “printl” (uten anførselstegn). I drop-down-menyen vil da “println():void PrintStream” være valgt. Trykk nedoverpil for å velge valg nummber to, dvs. “print(boolean arg0): void – PrintStream” og trykk RET igjen.

Teksten “arg0” vil være valgt. Skriv “Hello world!n” (denne gangen med anførselstegnene). Om du lurer på hvorfor man ikke skulle velge “print(string arg0)” så er svaret at man ikke trenger det. Det er nok å få på plass ett argument og så gi det en verdi av riktig type.

Trykk på “End”-tasten, for å gå til slutten av linja og skriv semikolon, dvs. “;” (uten anførselstegn).

Trykk Ctrl-S, for å lagre.

Merk: du kan godt skrive teksten

System.out.print("Hello world!n");

fullt ut. Men å bruke Ctrl-SPC og velge fra drop-down-menyen går raskere og vil eliminere skrivefeil.

Programmet ditt skal nå se sånn ut:

package no.example.hello;

public class Hello {

        /**
         * @param args
         */
        public static void main(String[] args) {
                System.out.print("Hello world!n");
        }

}

Nå har du lagd ditt første program og det er på tide å kjøre det. Fra menyen på toppen av Eclipse-vinduet, velg:

Run -> Run As -> Java Application

Nede i Eclipse vil det poppe opp et vindu som heter Console og teksten

Hello world!

vil vises.

En øvelse blir å bytte ut teksten og kjøre på nytt. Forslag til morsomme tekster, er “Hei verden!” og “God jul!”.

Tips: Ctrl-F11 er en snarveg for å kjøre sist kjørte prosjekt om igjen.

Laste ned og installere eclipse på Windows

English summary: This article is in Norwegian and describes downloading and installing the eclipse Java IDE.

Last ned eclipse herfra: http://www.eclipse.org/downloads/

Last ned “Eclipse Classic” i 32bits eller 64bits versjon (velg “mirror” på Universitetet i Oslo. Det går kjappest).

I tillegg så må du ha minst en Java5 RE (Runtime Edition), eller nyere. Om du har tenkt å prøve deg på Android-programmering, så må du installere en Java SDK (Software Development Kit). Hvis du ikke har tenkt å prøve deg på Android så klarer du deg med Runtime Edition (eclipse har sin egen Java-kompilator).

Merk: “Java5” betyr Java 1.5. Jeg testet på java 1.6, og det gikk helt greit. Java som du har installert for å kjøre applets i browsere er helt greit (igjen, som sagt, dersom du ikke skal programmere for Android).

Om du trenger ting fra andre varianter av eclipse (f.eks. JavaScript eller Android SDK eller noe sånt), så ikke tenk på det foreløpig. Alt til eclipse er skrevet som plugins. Og alt som er releaset gjennom eclipse kan lastes ned og installeres innefra eclipse. Masse opensource-plugins kan også lastes ned og installeres innefra eclipse.

Installasjonen består av å pakke ut zip-fila og så lage et shortcut til eclipse.exe på desktoppen. Ingen msi å kjøre.

Det betyr også at det er ganske greit å ha forskjellige versjoner av eclipse installert.

Emacs and lisp

Introduction

The Emacs text editor uses lisp as an extension language. This article will attempt to explain enough lisp to do basic emacs customization, to someone who knows imperative programming languages.

Evaluating lisp

Lisp consists of balanced pairs of parantheses, filled with tokens, separated by space, eg. like this:

(somefun1 1 2 3 "four")
(somefun2 5 6)

The flow of a lisp program is to start from the top, and evaluate the balanced parantheses and their contents. When doing this, lisp expects the first item in the list to either be a function (actually the name of a function (or actually a symbol bound to a function (but now it is time to stop digressing and nest up the parantheses and continue))), or what’s called “a special form”.

Evaluating functions

For now, let’s assume that the first items are all functions. Then the example above, would be to just invoke “somefun1” with three integer arguments and one string argument, then invoke “somefun2” with two integer arguments.

Ie. basically, the same as this C fragment, except with the parantheses surrounding the name of the function, and no semicolon to terminate each statement:

somefun(1, 2, 3, "four");
somefun(5, 6);

But a more interesting, or typical, lisp example, would be something like this:

(somefun1 (+ 0 1) (+ 1 1) (+ 1 1 1) (concat "f" "o" "u" "r"))
(somefun2 (- 11 6) (- 11 5))

What happens when lisp starts evaluating the first line, is that it will evaluate each of the balanced parantheses, before it eventually serves them up as arguments to “somefun1”.

Then lisp will move on to the next line and do the same thing there, and continue with this, until it reaches the end of input.

Evaluating “special forms”

Evaluating a special form differs from evaluating a function in the way the arguments are evaluated.

Examples of special forms, are the flow control constructs of lisp, eg. the “if” special form:

(if (= 2 2)
    (message "If test was true!")
  (message "If test was false!"))

When lisp encouters an “if” special clause, it will start by evaluating the first argument.

If the result of the first arguent is other than nil, the second argument will be evaluated. If the there are more arguments to “if”, they will not be evaluated.

If the result of the first argument is nil, the third, and following arguments to “if” will be evaluated (this is the “else” part of the if clause). In this case, the second argument to if will not be evaluated.

Trying out lisp

In emacs, go to the scratch buffer. This can be done with `C-x b *sc TAB RET’.

Type the following text into the buffer:

(message "Hello minibuffer!")

Place the cursor at the end of the line, and press `C-j’. Two things will happen:

  1. The minibuffer will show the text “Hello minibuffer!” (without the quotes)
  2. The line below the line you typed in will get the text “Hello minibuffer!” (this time with the quotes)

The `C-j’ command will evaluate the lisp expression that ends on the same line as the cursor, and the evaluation result will be printed on the next line.

The evaluation result is useful when debugging an expression, such as the test argument to an “if” special form.

If you don’t wish to move the cursor to the end of the clause being tested, you can instead press `C-M-x’, which will evaluate the outermost clause, under the cursor.

Using `C-M-x’ won’t output the evaluation result to the scratch buffer

Setting variables

In the .emacs file you can set variables that affect how emacs operates. On unix-ish systems, the .emacs file resides in the home directory. On windows systems the default is to look for the file in the “%USERPROFILE%AppDataRoaming” directory (unless the HOME environment variable, in which case emacs looks for it in “%HOME%”).

One of my oldest settings is one that stops emacs from saving “~” backup files:

(setq make-backup-files nil)

In today’s emacs one can set a simple settings like this interactively, using the customize mechanism.

The most important thing to be able to do, is to set various configuration variables. Today this can be done interactively for many variable using the “customize” mechanism.

But it’s still useful to be able to know how to set variables in lisp, eg. for setting variables differently depending of the type of file loaded, the version of emacs, or the OS one is running on.

To set variables, use the special form “setq”. Type the following into the scratch buffer, and evaluate it:

(setq a-variable-used-as-an-argument t)

Now a-variable-used-as-an-argument is a variable, and you can use it in lisp code. The value is t.

Defining functions

Functions are defined with the “defun” special form. An example, is:

(defun somefun1  (firstarg secondarg)
  "This is a function to do something.
If firstarg is non-nil it will output that firstarg was true.
If firstarg is nil it will output that firstarg was false.
The secondarg argument isn't used for anyting at all"
  (if firstarg
      (message "Firstarg was true!")
    (message "Firstarg was false!")))

First try `C-h f somefun1 RET’. Emacs will say “[No Match]” when RET is pressed.

Then move to the scratch buffer and paste in the code of the example, and use `C-j’ or `C-M-x’ to evaluate the defun clause.

Then try `C-h f somefun1 RET’ again. This time the emacs window will split in two and the lower half will show:

somefun1 is a Lisp function.

(somefun1 firstarg secondarg)

This is a function to do something.
If firstarg is non-nil it will output that firstarg was true.
If firstarg is nil it will output that firstarg was false.
The secondarg argument isn't used for anyting at all

[back]

To try using the function, paste the following into the scratch buffer, and evaluate it:

(somefun1)

This will cause the lisp debugger to pop up, saying that the function has two methods, but is called with none.

Add some arguments to the function call and evaluate it again:

(somefun1 nil "Required but unused")

The minibuffer will show “Firstarg was false!”. Now try putting different values as the first argument. All values, other than nil, will result in “Firstarg was true!” being written to the minibuffer.

Hooks

Many variables used by language modes are “buffer local”. This is so that eg. c-mode can use different indentation settings on two separate source files, to accomodate the different indentation/formatting used on them.

This means that if you set the variable when starting up emacs, the buffer version of the variable may not get the setting.

To make it possible to customize this, and other things, many emacs format specific modes provides “hooks”.

A hook is a list of functions, and emacs will run all functions in that list when doing a particular operation. Typically there are hooks for loading and saving files.

In summary

In the same way as the two preceeding articles, this article describes the subset of lisp and emacs lisp, that I use on a regular basis. Lisp by itself could probably be a lifetime study. I have for instance skipped mentioning macros, which are another important construct in lisp. Macros allow programmers to create constructs that behaves similar to special forms. Macros can e.g. be used to create new control structures (the macro “when” is such a control structure. It is nicer syntactically than the special form “if”).

Suggested reading is the Emacs Lisp Reference Manual and An Introduction to Programming in Emacs Lisp. In addition to reading them on the net, you can read them in the builtin manual reader of emacs (`C-h i m elisp RET’ and `C-h i m emacs lisp TAB RET’).