/*
 * AlignmentModel.java
 *
 * ...
 * ...
 * @author Oystein Reigem
 */

package aksis.alignment;

import java.awt.Color;
import java.util.*;
import java.io.*;
//import java.util.regex.*;
import javax.swing.*;
import java.awt.event.MouseEvent;
import java.lang.reflect.*;
import java.awt.Toolkit;   // beep
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.*;
import java.nio.charset.*;
import java.text.*;
import java.lang.Thread;

//import javax.swing.event.ListDataEvent;
import java.awt.Rectangle;

//////////////////////////////////////////////////////////////////////////////////////////
//apparatus for structure and flow of alignable elements through the alignment process //
//////////////////////////////////////////////////////////////////////////////////////////

/*
 class MyException extends Exception {
 
 public MyException() {
 }
 
 public MyException(String gripe) {
 super(gripe);
 }
 
 }
 */

class Aligned {
	
	/**
	 * lists of aligned elements.
	 * one list for each text.
	 * shown in the gui's 'aligned' JList components.
	 * each element is an AElement object.
	 */
	protected DefaultListModel[] elements;
	
	/**
	 * the finished alignments.
	 * each alignment is a Link object.
	 */
	List<Link> alignments = new ArrayList<Link>();   // package access. list of Link objects
	
	Aligned() {
		elements = new DefaultListModel[Alignment.NUM_FILES];
		for (int t=0; t<Alignment.NUM_FILES; t++) {
			elements[t] = new DefaultListModel();
		}
	}
	
	public void purge() {
		for (int t=0; t<Alignment.NUM_FILES; t++) {
			elements[t].clear();
		}
		alignments.clear();
	}
	
	public boolean hasHoles(Set[] integerSet) {
		
		// ¤¤¤ få kommentarer hjemmefra
		//System.out.println("ankommer hasHoles");
		// check if the n latest finished alignments
		// (the n bottom alignments)
		// have any holes.
		// for each text see if there are holes
		for (int t=0; t<Alignment.NUM_FILES; t++) {
			//System.out.println("t=" + t);
			if (((TreeSet)(integerSet[t])).size() > 0) {
				// there are elements in this text covered by the n latest alignments.
				// find the number of the highest element aligned for this text.
				// note that there might be crossing alignments,
				// so this element might not be covered by the n latest ones
				int high = ((AElement)(elements[t].get(elements[t].size()-1))).elementNumber;
				//System.out.println("high=" + high);
				// find the number of the lowest element covered by the n latest alignments
				int low = ((Integer)(((TreeSet)(integerSet[t])).last())).intValue();
				//System.out.println("low=" + low);
				// is there a hole?
				if (((TreeSet)(integerSet[t])).size() < (high-low+1)) {
					// yes
					//System.out.println("her er et hull");
					return true;
				}
			}
		}
		// no holes found
		//System.out.println("fant ingen hull");
		return false;
		
	}
	
	//AlignmentsEtc drop() {
	AlignmentsEtc drop(AlignGui gui) {
		//System.out.println("drop()");
		// can't just go ahead and drop bottom alignment.
		// must make sure there are no holes due to crossing alignments.
		// scan alignments from bottom upwards until there are no holes.¤¤¤
		if (alignments.size() == 0) {
			//
			Toolkit.getDefaultToolkit().beep();
			//System.out.println("BEEEEEEEEEEEEEEEEP Aligned drop");
			System.out.println("Nothing to drop");
			return null;
		} else {
			Set[] testElementNumbers = new TreeSet[Alignment.NUM_FILES];   // TreeSet is a sorted Set
			for (int t=0; t<Alignment.NUM_FILES; t++) {
				Set<Integer> s = new TreeSet<Integer>();   // TreeSet is a sorted Set
				testElementNumbers[t] = s;
				//System.out.println("testElementNumbers["+t+"] = " + testElementNumbers[t]);
			}
			boolean holes = true;   // pessimistic ¤¤¤
			////System.out.println("alignments.size() = " + alignments.size());
			// find the first alignment to drop
			//System.out.println("skal gå i løkke og ta én alignment om gangen. starter med ingen");
			int numberOfFirstAlignmentToDrop = alignments.size() - 1;
			while (holes) {
				//System.out.println("løkkegjennomløp for å se om vi har hull hvis vi tar med en alignment til");
				if (numberOfFirstAlignmentToDrop < 0) {
					//System.err.println("*** Program error 1");
					ErrorMessage.programError("AlignmentsEtc drop(). numberOfFirstAlignmentToDrop < 0");   // 2006-08-10
					return null;
				}
				for (int t=0; t<Alignment.NUM_FILES; t++) {
					testElementNumbers[t].addAll(((Link)(alignments.get(numberOfFirstAlignmentToDrop))).getElementNumbers(t));
					//System.out.println("testElementNumbers["+t+"] = " + testElementNumbers[t]);
				}
				if (!hasHoles(testElementNumbers)) {   // ¤¤¤var ikke bra nok. høyeste element-nummer må også være med. ellers oppdages ikke kryssing likevel
					holes = false;
				} else {
					numberOfFirstAlignmentToDrop--;
				}
			}
			//
			AlignmentsEtc returnValue = new AlignmentsEtc();
			// elements belonging to alignments starting with number 'numberOfFirstAlignmentToDrop' are to be dropped
			//System.out.println("skal gå i dobbel løkke og ta ett element om gangen over til returnValue.....");
			for (int t=0; t<Alignment.NUM_FILES; t++) {
				//System.out.println("t=" + t);
				//System.out.println("numberOfFirstAlignmentToDrop=" + numberOfFirstAlignmentToDrop);
				if (((TreeSet)(testElementNumbers[t])).size() > 0) {
					// there are elements to drop
					int numberOfFirstElementToDrop = ((Integer)(((TreeSet)(testElementNumbers[t])).first())).intValue();
					//System.out.println("numberOfFirstElementToDrop=" + numberOfFirstElementToDrop);
					int numberOfLastElementToDrop = ((Integer)(((TreeSet)(testElementNumbers[t])).last())).intValue();
					//System.out.println("numberOfLastElementToDrop=" + numberOfLastElementToDrop);
					for (int j=numberOfFirstElementToDrop; j<=numberOfLastElementToDrop; j++) {
						//System.out.println("drops element");
						//AElement elementToDrop = (AElement)elements[t].remove(elements[t].size()-1);
						((DefaultListModel)(returnValue.elements[t])).addElement((AElement)(elements[t].remove(numberOfFirstElementToDrop)));   // ###
					}
				}
			}
			// alignments starting with number 'numberOfFirstAlignmentToDrop' are to be dropped
			//System.out.println("skal gå i løkke og ta én alignment om gangen over til returnValue.alignments");
			while (alignments.size() > numberOfFirstAlignmentToDrop) {
				//System.out.println("drops alignment");
				returnValue.alignments.add(returnValue.alignments.size(), alignments.remove(numberOfFirstAlignmentToDrop));
			}
			// update aligned/total ratio in status line
			gui.model.setMemoryUsage(gui);   // 2006-10-03
			gui.model.updateAlignedTotalRatio(gui);
			//
			return returnValue;
		}
	}
	
	void pickUp(AlignGui gui, AlignmentsEtc valueGot, boolean scroll) {
		//System.out.println("pickUp A");
		//System.out.println("pickUp()");
		//MemTest.print("Tenured Gen", "");
		if (valueGot != null) {
			// alignments
			//System.out.println("pickUp B");
			//MemTest.print("Tenured Gen", "");
			//######hvorfor tom løkke?
			for (int j=0; j<valueGot.alignments.size(); j++) {
				//System.out.println("skal legge til alignment nr " + ((Link)(valueGot.alignments.get(j))).alignmentNumber);
			}
			//System.out.println("pickUp C");
			//MemTest.print("Tenured Gen", "");
			alignments.addAll(alignments.size(), valueGot.alignments);
			//System.out.println("pickUp D");
			//MemTest.print("Tenured Gen", "");
			// elements
			for (int t=0; t<Alignment.NUM_FILES; t++) {
				//System.out.println("pickUp");
				//System.out.println("size of no " + t + " = " + ((DefaultListModel)(valueGot.elements[t])).size());
				//System.out.println("pickUp E");
				//MemTest.print("Tenured Gen", "");
				for (int i=0; i<((DefaultListModel)(valueGot.elements[t])).size(); i++) {
					//elements[t].addElement((AElement)(valueGot.elements[t]));
					//System.out.println("picks up " + (AElement)(((DefaultListModel)(valueGot.elements[t])).get(i)));
					elements[t].addElement(((AElement)((DefaultListModel)(valueGot.elements[t])).get(i)));   // ###
					//System.out.println("pickUp F");
					//MemTest.print("Tenured Gen", "");
				}
				// scroll the list box so the bottom shows
				//System.out.println("pickUp F2");
				//MemTest.print("Tenured Gen", "");
				//if (scroll) { // ### saboterer denne og ser hvordan det går
				if (true) {
					//System.out.println("scroll");
					gui.alignedListBox[t].ensureIndexIsVisible(elements[t].getSize()-1);
				}
				//System.out.println("pickUp G");
				//MemTest.print("Tenured Gen", "");
				//System.out.println("x");
				//gui.alignedListBox[t].repaint();   // ### forsøk på å få aligned til å oppdatere seg etterhver enkeltaligning
				//gui.alignedListBox[t].paintImmediately(new Rectangle(100, 100));
				//gui.alignedListBox[t].paintImmediately(gui.alignedListBox[t].getBounds()); funker til en viss grad. men ruller ikke
				
				//!!// ### forsøk på å lytte på events
				//!!//ListDataEvent listDataEvent;
				//!!//gui.alignedListBox[t].AccessibleJList.contentsChanged(listDataEvent);
				//!!//System.out.println("listDataEvent = " + listDataEvent);
			}
			// update aligned/total ratio in status line
			//System.out.println("pickUp() kaller updateAlignedTotalRatio()");
			gui.model.setMemoryUsage(gui);   // 2006-10-03
			gui.model.updateAlignedTotalRatio(gui);
		}
		//System.out.println("pickUp H");
		//MemTest.print("Tenured Gen", "");
	}
	
}

class ToAlign {
	
	/**
	 * lists of elements under consideration for alignment.
	 * one list for each text.
	 * shown in the gui's 'toAlign' JList components.
	 * each element is an AElement object.
	 */
	protected DefaultListModel[] elements;
	
	/**
	 * 0 or more alignments.
	 * each alignment is a Link object.
	 */
	private List<Link> pending = new ArrayList<Link>();
	
	///**
	// * the unaligned elements.
	// * also represented by a Link object.
	// */
	//private Link unused = new Link();
	
	/**
	 * the number of the first pending alignment.
	 * if no alignments pending, the number to use when establishing a pending alignment,
	 * i.e, one higher than the highest number of the finished alignments.
	 */
	private int firstAlignmentNumber = 0;
	
	ToAlign() {
		elements = new DefaultListModel[Alignment.NUM_FILES];
		for (int t=0; t<Alignment.NUM_FILES; t++) {
			elements[t] = new DefaultListModel();
		}
	}
	
	public void purge() {
		for (int t=0; t<Alignment.NUM_FILES; t++) {
			elements[t].clear();
		}
		pending.clear();
		firstAlignmentNumber = 0;
	}
	
	/**
	 * is all (both) to-align boxes empty?
	 */
	boolean empty() {
		//System.out.println("ToAlign.empty() kalt");
		//System.out.println("pending.size() = " + pending.size());
		//System.out.println("unused.empty() = " + unused.empty());
		//return (pending.size() == 0 && unused.empty());
		return (pending.size() == 0);
	}
	
	/**
	 * is to-align box number t empty?
	 */
	// ###needed for correction of bug 2006-03-28
	// ###kanskje bedre å kikke i boksens liste...
	boolean empty(int t) {
		// search through pending alignments for one that has element(s) in text t
		for (int ii=0; ii < pending.size(); ii++) {
			Link link = (Link)(pending.get(ii));
			if (link.elementNumbers[t].size() > 0) {
				// found one
				return false;
			}
		}
		// searched through all but found none
		return true;
	}
	
	// #######bør sikkert brukes mer
	
	int pendingCount() {
		return pending.size();
	}
	
	// ¤¤¤ 2004-10-31. brukes ikke - i hvert fall ikke for øyeblikket
	void link(AlignGui gui, int t, int indexClicked, int elementNumberClicked) {   // package access
		// link by default to uppermost pending alignment
		//System.out.println("firstAlignmentNumber = " + firstAlignmentNumber);
		link(gui, t, indexClicked, elementNumberClicked, firstAlignmentNumber);
	}
	
	// ¤¤¤ 2004-10-31. bare denne brukes.
	// ¤¤¤ denne programmeringen trenger sikkert rydding.
	// for mye som ikke er oop?
	void link(AlignGui gui, int t, int indexClicked, int elementNumberClicked, int alignmentNumberToLinkTo) {   // package access
		//System.out.println("ToAlign sin link()");
		//System.out.println("pending før at link() har gjort sin jobb:");
		for (int ii=0; ii < pending.size(); ii++) {
			//System.out.println("pending alignment nr " + ii + " er " + (Link)(pending.get(ii)));
		}
		// ¤¤¤foreløpig
		// redundans: indexClicked (nr på item i JList) og elementNumberClicked
		//System.out.println("indexClicked = " + indexClicked);
		//System.out.println("elementNumberClicked = " + elementNumberClicked);
		//System.out.println("alignmentNumberToLinkTo = " + alignmentNumberToLinkTo);
		//System.out.println("t = " + t);
		// clicked on unused or pending? ¤¤¤ skal ikke være noen unused lenger. i.g.n.m
		int alignmentNumberClicked = ((AElement)(elements[t].get(indexClicked))).alignmentNumber;
		//System.out.println("alignmentNumberClicked = " + alignmentNumberClicked);
		// ...
		int lastAlignmentNumber = firstAlignmentNumber + pending.size() - 1;
		//System.out.println("lastAlignmentNumber = " + lastAlignmentNumber);
		// ...
		//System.out.println("håndtering av alignmentNumberToLinkTo == -2");
		if (alignmentNumberToLinkTo == -2) {
			// -2 is a signal to link to the next available alignment
			if (   (alignmentNumberClicked == lastAlignmentNumber)
					&& (((Link)(pending.get(lastAlignmentNumber - firstAlignmentNumber))).countElements() <= 1)) {   // == 1
				//System.out.println("has run out of alignments. back to the first one");
				// has run out of alignments.
				// back to the first one
				//alignmentNumberToLinkTo = 0; ###
				alignmentNumberToLinkTo = firstAlignmentNumber;
			} else {
				//System.out.println("not run out of alignments");
				alignmentNumberToLinkTo = alignmentNumberClicked + 1;
				//System.out.println("alignmentNumberToLinkTo = " + alignmentNumberToLinkTo);
			}
		}
		//System.out.println("alignmentNumberToLinkTo = " + alignmentNumberToLinkTo);
		// might have to create new pending
		if (alignmentNumberToLinkTo > pending.size() - 1) {
			pending.add(new Link());
		}
		// change alignment
		//System.out.println("change alignment. alignment før = " + (Link)(pending.get(alignmentNumberClicked - firstAlignmentNumber)));
		//System.out.println("t = " + t);
		//System.out.println("alignmentNumberClicked = " + alignmentNumberClicked);
		//System.out.println("elementNumberClicked = " + elementNumberClicked);
		//System.out.println("firstAlignmentNumber = " + firstAlignmentNumber);
		//System.out.println("alignmentNumberToLinkTo = " + alignmentNumberToLinkTo);
		((Link)(pending.get(alignmentNumberClicked - firstAlignmentNumber))).elementNumbers[t].remove(new Integer(elementNumberClicked));
		//((Link)(pending.get(alignmentNumberClicked - firstAlignmentNumber))).elementNumbers[t].remove(new Integer(indexClicked));   // endret 2005-05-31. nei, det er tull. vi jobber på et Set, der nøklene er elementnummer, ikke indeks
		((Link)(pending.get(alignmentNumberToLinkTo - firstAlignmentNumber))).elementNumbers[t].add(new Integer(elementNumberClicked));
		//((Link)(pending.get(alignmentNumberToLinkTo - firstAlignmentNumber))).elementNumbers[t].add(new Integer(indexClicked));   // endret 2005-05-31. nei, det er tull. vi jobber på et Set, der nøklene er elementnummer, ikke indeks
		//((Link)(pending.get(alignmentNumberToLinkTo - firstAlignmentNumber))).alignmentNumber = alignmentNumberToLinkTo;
		//System.out.println("change alignment. alignment etter = " + (Link)(pending.get(alignmentNumberClicked - firstAlignmentNumber)));
		// check if the change creates a hole in the list of pending alignments,
		// i.e, a pending alignment with no elements.
		// if so, remove hole
		//System.out.println("sjekk om hull");
		for (int ii=0; ii < pending.size(); ) {
			if (((Link)(pending.get(ii))).countElements() == 0) {
				// hole. remove
				// ### 2006-03-30.
				// ### oppdaget at jeg et _annet_ sted tok pending.remove(...) med fatale konsekvenser.
				// ### det ble nemlig hull i nummereringen av alignments.
				// ### (man droppet et element som ikke var linket mot noe annet element
				// ### (det inngikk altså i en 1-0- eller 0-1-alignment),
				// ### og da forsvant hele alignmenten,
				// ### men jeg hadde ikke tenkt på at jeg da måtte renummerere
				// ### de etterfølgende alignments i pending.
				// ### men her på _dette_ stedet rydder jeg vel grundig opp?
				pending.remove(ii);
				// (repeat with same ii)
			} else {
				ii++;
			}
		}
		// try to maintain a natural order to the pending alignments.
		//
		// there might not exist a canonical natural order
		// for alignments, though.
		// say two texts contain elements that are cross-aligned like this:
		//   A     B
		//   B     A
		// what is the natural order of the alignments?
		//
		// is it A before B?:
		//   A--A  B
		//       \/
		//       /\
		//   B--B  A
		//
		// or is it B before A?:
		//   A  B--B
		//    \/
		//    /\
		//   B  A--A
		//
		// the programming here will let the order be decided by the first text,
		// i.e, let the order of alignments be decided by the order
		// of their elements in the first text:
		//   A--A  B
		//       \/
		//       /\
		//   B--B  A
		//
		// a more precise rule is needed when alignments
		// have more than one element in each text:
		// then the order is decided by each alignment's
		// *first* element in the first text.
		//
		// alignments without any elements in the first text
		// might be more problematic.
		// example:
		// A      B
		// B      C
		//        A
		//
		// the programming here will put all alignments
		// with elements in the first text
		// before alignments without any elements in the first text:
		//   A--A  B
		//       \/
		//       /\
		//   B--B  A
		//
		//      C--C
		//
		// the latter alignments' order is decided by the order
		// of the elements in the second text.
		/*
		 skitt
		 A B
		 A A
		 her havner B sist
		 her er det ikke noe som krysser.
		 det kan gjøre det lettere
		 eller overlate fullstendig til brukeren?
		 */
		//System.out.println("prøv å bevare naturlig rekkefølge");
		List<String> toSort = new ArrayList<String>();
		for (int ii=0; ii < pending.size(); ii++) {
			// next pending alignment
			//System.out.println("next pending alignment. ii=" + ii);
			Link link = (Link)(pending.get(ii));
			// find this alignment's first text
			for (int tt=0; tt<Alignment.NUM_FILES; tt++) {
				if (link.elementNumbers[tt].size() > 0) {
					int firstElementNumber = ((Integer)(((TreeSet)(link.elementNumbers[tt])).first())).intValue();
					String firstElementNumberString = Integer.toString(1000000 + firstElementNumber).substring(1);
					toSort.add(Integer.toString(tt) + "-" + firstElementNumberString + "-" + ii);
					break;
				}
			}
		}
		// sort
		List<String> sorted = new ArrayList<String>();
		Collections.sort(toSort);
		sorted = toSort;
		// reorder the pending alignments
		List<Link> newPending = new ArrayList<Link>();
		for (int ii=0; ii < sorted.size(); ii++) {
			//System.out.println("neste sorterte. ii=" + ii);
			String tempStr = (String)(sorted.get(ii));
			//System.out.println("tempStr = " + tempStr);
			String[] tempArr = tempStr.split("-");
			int tempInt = Integer.parseInt(tempArr[2]);
			//System.out.println("tempInt = " + tempInt);
			newPending.add(pending.get(tempInt));
			//((Link)(newPending.get(ii))).alignmentNumber = ii;
			((Link)(newPending.get(ii))).alignmentNumber = ii + gui.model.aligned.alignments.size();   // endret 2005-05-31. bug som har overlevd lenge. har visst ikke prøvd toAlign-klikking når det står noe i aligned. alignmentnummer i toAlign starter på # aligned alignments, ikke nødvendigvis på 0
		}
		//System.out.println("pending før sort =");
		for (int ii=0; ii < pending.size(); ii++) {
			//System.out.println("pending.get(" + ii + ") =" + (Link)(pending.get(ii)));
		}
		pending = newPending;
		//System.out.println("pending etter sort =");
		for (int ii=0; ii < pending.size(); ii++) {
			//System.out.println("pending.get(" + ii + ") =" + (Link)(pending.get(ii)));
		}
		// update and refresh elements
		for (int ii=0; ii < pending.size(); ii++) {
			//System.out.println("neste refresh elements");
			for (int tt=0; tt<Alignment.NUM_FILES; tt++) {
				//System.out.println("tt=" + tt);
				Iterator e = ((TreeSet)(((Link)(pending.get(ii))).getElementNumbers(tt))).iterator();
				//System.out.println("skaffet iterator");
				while (e.hasNext()) {
					//System.out.println("har en neste");
					int elementNumber = ((Integer)(e.next())).intValue();
					//System.out.println("elementNumber=" + elementNumber);
					int index = elementNumber - gui.model.aligned.elements[tt].size();   // endret 2005-05-31. bug som har overlevd lenge. har visst ikke prøvd toAlign-klikking når det står noe i aligned
					//((AElement)(elements[tt].get(elementNumber))).alignmentNumber = ((Link)(pending.get(ii))).alignmentNumber;
					((AElement)(elements[tt].get(index))).alignmentNumber = ((Link)(pending.get(ii))).alignmentNumber;   // endret 2005-05-31. bug som har overlevd lenge. har visst ikke prøvd toAlign-klikking når det står noe i aligned
					// shake it so the colour changes (remove + add)
					//elements[tt].add(elementNumber ,elements[tt].remove(elementNumber));
					elements[tt].add(index, elements[tt].remove(index));   // endret 2005-05-31. bug som har overlevd lenge. har visst ikke prøvd toAlign-klikking når det står noe i aligned
				}
			}
		}
		//System.out.println("pending etter at link() har gjort sin jobb:");
		for (int ii=0; ii < pending.size(); ii++) {
			//System.out.println("pending alignment nr " + ii + " er " + (Link)(pending.get(ii)));
		}
		
		// ¤¤¤ kuttet programmering som har med unused å gjøre.
		// får vi bruk for det igjen?
		/*
		 ((AElement)(elements[t].get(indexClicked))).alignmentNumber = alignmentNumberToLinkTo;   // ¤¤¤foreløpig. // ¤¤¤ og mangler det også noe som trigger gjenoppfrisking?
		 System.out.println("alignmentNumberClicked = " + alignmentNumberClicked);
		 if (alignmentNumberClicked == -1) {
		 // unused
		  System.out.println("unused 1");
		  System.out.println("unused 2");
		  unused.elementNumbers[t].remove(new Integer(elementNumberClicked));   // ¤¤¤foreløpig
		  System.out.println("unused 3");
		  if (pending.size() == 0) {   // ¤¤¤foreløpig
		  pending.add(new Link());   // ¤¤¤foreløpig
		  }   // ¤¤¤foreløpig
		  // ¤¤¤ foreløpig har pending aldri mer enn én Link. jo, det kan være flere!
		   ((Link)(pending.get(0))).elementNumbers[t].add(new Integer(elementNumberClicked));
		   ((Link)(pending.get(0))).alignmentNumber = alignmentNumberToLinkTo;
		   System.out.println("unused 4");
		   System.out.println("unused 5");
		   } else {
		   // pending
		    System.out.println("pending");
		    System.out.println("pending.size()=" + pending.size());
		    for (int k = 0; k < pending.size(); k++) { System.out.println("pending.get(" + k + ")=" + pending.get(k)); }
		    System.out.println("alignmentNumberClicked=" + alignmentNumberClicked);
		    System.out.println("firstAlignmentNumber=" + firstAlignmentNumber);
		    System.out.println("elementNumberClicked=" + elementNumberClicked);
		    if (alignmentNumberToLinkTo > lastAlignmentNumber) {
		    pending.add(new Link());
		    }
		    ((Link)(pending.get(alignmentNumberClicked - firstAlignmentNumber))).elementNumbers[t].remove(new Integer(elementNumberClicked));   // ¤¤¤foreløpig
		    System.out.println("pending 2");
		    ((Link)(pending.get(0))).elementNumbers[t].add(new Integer(elementNumberClicked));   // ¤¤¤foreløpig
		    System.out.println("pending 3");
		    ((Link)(pending.get(0))).alignmentNumber = alignmentNumberToLinkTo;   // ¤¤¤foreløpig
		    System.out.println("pending 4");
		    if (((Link)(pending.get(alignmentNumberClicked - firstAlignmentNumber))).empty()) {
		    // a pending Link collapses
		     System.out.println("a pending Link collapses");
		     pending.remove(alignmentNumberClicked - firstAlignmentNumber);£££££££££££££££££
		     // renumber the following Link's
		      for (int i=alignmentNumberClicked - alignmentNumberToLinkTo; i<pending.size(); i++) {
		      ((Link)(pending.get(i))).alignmentNumber--;
		      for (int tt=0; tt<Alignment.NUM_FILES; tt++) {
		      Iterator e = ((TreeSet)(((Link)(pending.get(i))).getElementNumbers(tt))).iterator();
		      while (e.hasNext()) {
		      int elementNumber = ((Integer)(e.next())).intValue();
		      ((AElement)(elements[tt].get(elementNumber))).alignmentNumber = ((Link)(pending.get(i))).alignmentNumber;
		      // shake it so the colour changes (remove + add)
		       elements[tt].add(elementNumber ,elements[tt].remove(elementNumber));
		       }
		       }
		       }
		       }
		       }
		       //
		        */
	}
	
	//AElement drop(int t) {
	AElement drop(AlignGui gui, int t) {   // ### 2006-03-30
		//System.out.println("AElement.drop(). t = " + t);
		//if (empty()) {
		if (empty(t)) {   // ###bug corrected 2006-03-28
			Toolkit.getDefaultToolkit().beep();
			//System.out.println("BEEEEEEEEEEEEEEEEP ToAlign drop");
			//System.out.println("Nothing to drop");
			return null;
		} else {
			//System.out.println("elements[t].size() = " + elements[t].size());
			AElement elementToDrop = (AElement)elements[t].remove(elements[t].size()-1);
			//System.out.println("elementToDrop = " + elementToDrop);
			//System.out.println("elements[t].size() = " + elements[t].size());
			if (elementToDrop.alignmentNumber == -1) {
				// the element to drop is unused
				//unused.elementNumbers[t].remove(new Integer(elementToDrop.elementNumber));
				System.out.println("*** Program error ***");
			} else {
				// the element to drop is pending.
				// where in the 'pending' list is its Link?
				//System.out.println("elementToDrop.alignmentNumber = " + elementToDrop.alignmentNumber);
				//System.out.println("((Link)(pending.get(0))).alignmentNumber = " + ((Link)(pending.get(0))).alignmentNumber);
				int index = elementToDrop.alignmentNumber - ((Link)(pending.get(0))).alignmentNumber;
				//System.out.println("index = " + index);
				// remove element from its pending alignment
				((Link)(pending.get(index))).getElementNumbers(t).remove(new Integer(elementToDrop.elementNumber));
				// check if there is anything left of the pending alignment
				//System.out.println("check if there is anything left of the pending alignment");
				if (((Link)(pending.get(index))).empty()) {
					// nothing. remove it
					//System.out.println("nothing. remove it");
					pending.remove(index);
					// ### 2006-03-30.
					// renumber the pending alignments that follow!
					for (int ii=index; ii < pending.size(); ii++) {
						((Link)(pending.get(ii))).alignmentNumber--;
						// don't forget to update the alignment numbers
						// in the elements that belong to this alignment
						for (int tt=0; tt<Alignment.NUM_FILES; tt++) {
							Iterator e = ((TreeSet)(((Link)(pending.get(ii))).getElementNumbers(tt))).iterator();
							while (e.hasNext()) {
								int elementNumber = ((Integer)(e.next())).intValue();
								int indexx = elementNumber - gui.model.aligned.elements[tt].size();
								((AElement)(elements[tt].get(indexx))).alignmentNumber = ((Link)(pending.get(ii))).alignmentNumber;
								// shake the element so the colour changes (remove + add)
								elements[tt].add(indexx, elements[tt].remove(indexx));
							}
						}
					}
				}
			}
			return elementToDrop;
		}
	}
	
	//void pickUp(AlignGui gui, int t, AElement element) {
	void pickUp(int t, AElement element) {
		if (element != null) {
			//System.out.println("ToAlign sin pickUp");
			elements[t].add(elements[t].size(), element);
			//System.out.println("ToAlign sin pickUp 2");
			//unused.elementNumbers[t].add(new Integer(element.elementNumber));
			//System.out.println("ToAlign sin pickUp 3");
			// include the element in the last pending alignment.
			// create a pending alignment if none exists
			if (pending.size() == 0) {
				//System.out.println("create a pending alignment");
				pending.add(new Link());
				((Link)(pending.get(0))).alignmentNumber = firstAlignmentNumber;
			}
			//System.out.println("ToAlign sin pickUp 4");
			((Link)(pending.get(pending.size() - 1))).elementNumbers[t].add(new Integer(element.elementNumber));
			//System.out.println("ToAlign sin pickUp 5");
			int lastAlignmentNumber = firstAlignmentNumber + pending.size() - 1;
			//System.out.println("ToAlign sin pickUp 6");
			element.alignmentNumber = lastAlignmentNumber;
			//System.out.println("ToAlign sin pickUp 7");
			//gui.model.link(gui, t, elements[t].size() - 1, element.elementNumber);
			//System.out.println("t=" + t);
			//System.out.println("elements[t].size()=" + elements[t].size());
			//System.out.println("element.elementNumber=" + element.elementNumber);
		}
	}
	
	// ###ment for å ta imot fra aligned, men vil bruke det for someAligned også?
	void catch_(AlignmentsEtc valueGot) {
		if (valueGot != null) {
			// alignments
			//System.out.println("valueGot.alignments.size() = " + valueGot.alignments.size());
			//System.out.println("tar pending.addAll(...)");
			pending.addAll(0, valueGot.alignments);
			// elements
			for (int t=0; t<Alignment.NUM_FILES; t++) {
				//System.out.println("((DefaultListModel)(valueGot.elements[" + t + "])).size() = " + ((DefaultListModel)(valueGot.elements[t])).size());
				for (int i=0; i<((DefaultListModel)(valueGot.elements[t])).size(); i++) {
					//System.out.println("skal ta elements[t].add(" + i + ", ...)");
					//System.out.println("testDefaultListModel");
					DefaultListModel testDefaultListModel = (DefaultListModel)(valueGot.elements[t]);
					//System.out.println("tester testObject");
					Object testObject = ((DefaultListModel)(valueGot.elements[t])).get(i);
					//System.out.println("tester testAElement");
					AElement testAElement = (AElement)(((DefaultListModel)(valueGot.elements[t])).get(i));
					//System.out.println("har testet testAElement");
					elements[t].add(i, (AElement)(((DefaultListModel)(valueGot.elements[t])).get(i)));   // ###
				}
			}
			firstAlignmentNumber = ((Link)(pending.get(0))).alignmentNumber;
			//System.out.println("nå ble firstAlignmentNumber = " + firstAlignmentNumber);
		}
	}
	
	/**
	 * pops everything,
	 */
	AlignmentsEtc flush() {
		//System.out.println("flush A");
		//MemTest.print("Tenured Gen", "");
		//if (!unused.empty()) {
		//	// ...
		//	//System.out.println("unused ikke tom");
		//	Toolkit.getDefaultToolkit().beep();
		//	System.out.println("Program error. 'unused' not empty");
		//	//System.out.println("BEEEEEEEEEEEEEEEEP ToAlign flush 1");
		//	return null;
		//} else {
		//System.out.println("unused tom");
		//System.out.println("flush B");
		//MemTest.print("Tenured Gen", "");
		if (pending.size() == 0) {
			//System.out.println("pending tom");
			Toolkit.getDefaultToolkit().beep();
			System.out.println("Nothing to flush");
			//System.out.println("BEEEEEEEEEEEEEEEEP ToAlign flush 2");
			return null;
		} else {
			//System.out.println("skal flushe");
			// update firstAlignmentNumber
			firstAlignmentNumber = ((Link)(pending.get(pending.size() - 1))).alignmentNumber + 1;
			//System.out.println("flush. nå ble firstAlignmentNumber = " + firstAlignmentNumber);
			// update ...
			//System.out.println("flush C");
			//MemTest.print("Tenured Gen", "");
			AlignmentsEtc returnValue = new AlignmentsEtc();
			//System.out.println("flush D");
			//MemTest.print("Tenured Gen", "");
			while (pending.size() > 0) {
				returnValue.alignments.add(pending.remove(0));
				//System.out.println("flush E");
				//MemTest.print("Tenured Gen", "");
			}
			//System.out.println("nå er pending.size() = " + pending.size());
			for (int t=0; t<Alignment.NUM_FILES; t++) {
				//((DefaultListModel)(returnValue.elements[t])).addAll((DefaultListModel)(elements[t]));
				while (((DefaultListModel)(elements[t])).size() > 0) {
					((DefaultListModel)(returnValue.elements[t])).addElement((AElement)(elements[t].remove(0)));
					//System.out.println("flush F");
					//MemTest.print("Tenured Gen", "");
				}
				//System.out.println("nå er elements[" + t + "].size() = " + elements[t].size());
			}
			//System.out.println("flush G");
			//MemTest.print("Tenured Gen", "");
			return returnValue;
		}
		//}
	}
	
}

class Unaligned {
	
	/**
	 * lists of unaligned elements.
	 * one list for each text.
	 * shown in the gui's 'unaligned' JList components.
	 * each element is an AElement object.
	 */
	protected DefaultListModel[] elements;   // ########## private + get-metode er bedre
	
	Unaligned() {
		elements = new DefaultListModel[Alignment.NUM_FILES];
		for (int t=0; t<Alignment.NUM_FILES; t++) {
			elements[t] = new DefaultListModel();
		}
	}
	
	public void purge() {
		for (int t=0; t<Alignment.NUM_FILES; t++) {
			elements[t].clear();
		}
	}
	
	/**
	 * pop the top unaligned AElement of the t-th text
	 */
	AElement pop(int t) {
		
		if (elements[t].size() == 0) {
			Toolkit.getDefaultToolkit().beep();
			//System.out.println("BEEEEEEEEEEEEEEEEP Unaligned pop");
			System.out.println("Nothing to pop");
			return null;
		} else {
			return (AElement)elements[t].remove(0);
		}
		
	}
	
	/**
	 * insert an AElement at the top of the t-th text.
	 * set the element's state to unused,
	 * no matter what its previous state was.
	 */
	void catch_(int t, AElement element) {
		if (element != null) {
			element.alignmentNumber = -1;   // ¤¤¤bruke static konstant?
			elements[t].add(0, element);   // ### æsj. her trengs ingen (AElement)
		}
	}
	
	/**
	 * ...
	 */
	void add(int t, AElement element) {
		elements[t].addElement((Object)element);
	}
	
	///**
	//  * get the i'th AElement in total
	//  */
	//AElement get(AlignmentModel model, int t, int i) {
	//	... + model.toAlign.elements[0].getSize() ...
	//}
	
	public AElement get(int t, Node element) {
		// searches unaligned of text t
		// for an AElement containing a certain alignable element
		////Iterator it = elements[t].iterator();
		for (Enumeration en = elements[t].elements(); en.hasMoreElements();) {
			AElement test = (AElement)en.nextElement();
			if (test.element == element) {
				// success
				return test;
			}
		}
		// failure
		return null;
	}
	
}

/**
 * a class that bundles alignments with their AElement elements.
 * used for movement of data between aligned and to-align (alignments under manual consideration).
 * ###also used for movement of data from unaligned to toAlign when skipping half-aligned file
 * ###ikke god. redundans og mulighet for inkonsistens i alignments vs elements.
 */
class AlignmentsEtc {
	
	List<Link> alignments = new ArrayList<Link>();
	Object[] elements;
	
	AlignmentsEtc() {
		elements = new DefaultListModel[Alignment.NUM_FILES];
		for (int t=0; t<Alignment.NUM_FILES; t++) {
			elements[t] = new DefaultListModel();
		}
	}
	
	// ###brukes ikke overalt
	
	public void add(Link link) {
		alignments.add(link);
	}
	
	// ###brukes ikke overalt
	//public void add(int t, Element element) {
	public void add(int t, AElement element) {
		if (!((DefaultListModel)elements[t]).contains(element)) {
			((DefaultListModel)elements[t]).addElement(element);
		}
	}
	
	public boolean hasHoles() {
		
		TreeSet<Integer>[] testElementNumbers;
		testElementNumbers= new TreeSet[Alignment.NUM_FILES];   // TreeSet is a sorted Set
		for (int t=0; t<Alignment.NUM_FILES; t++) {
			testElementNumbers[t] = new TreeSet<Integer>();
		}
		
		// loop through the alignments
		Iterator it = alignments.iterator();
		while(it.hasNext()) {
			Link link = (Link)it.next();
			for (int t=0; t<Alignment.NUM_FILES; t++) {
				Iterator it2 = link.getElementNumbers(t).iterator();
				while(it2.hasNext()) {
					Integer n = (Integer)it2.next();
					testElementNumbers[t].add(n);
				}
			}
		}
		
		// ###the hasHoles method should have been a utility thing,
		// not belong to the Aligned object.
		//###uh. Aligned's hasHoles is more complicated
		//return Aligned.hasHoles(testElementNumbers);
		for (int t=0; t<Alignment.NUM_FILES; t++) {
			if (testElementNumbers[t].size() > 0) {
				int last = ((Integer)(testElementNumbers[t].last())).intValue();
				int first = ((Integer)(testElementNumbers[t].first())).intValue();
				if ((last - first + 1) != testElementNumbers[t].size()) {
					// found hole for text t
					return true;
				}
			}
		}
		// found no hole for any text
		return false;
		
	}
	
	public boolean empty() {
		return (alignments.size() == 0);
	}
	
	// for debugging purposes
	public void print() {
		// loop through the alignments
		Iterator it = alignments.iterator();
		System.out.println("<<<link");
		while(it.hasNext()) {
			Link link = (Link)it.next();
			System.out.println(link);
		}
		System.out.println("link>>>");
		// loop ... elements
		for (int t=0; t<Alignment.NUM_FILES; t++) {
			System.out.println("<<<text " + t);
			DefaultListModel l = (DefaultListModel)elements[t];
			for (int i=0; i<l.size(); i++) {
				System.out.println(l.get(i));
			}
			System.out.println("text " + t + ">>>");
		}
	}
	
}

/**
 * the program works with elements from xml files, e.g sentences.
 * each element is a node in a DOM tree.
 * but the program also needs to know which alignment each element is involved in, if any.
 * for this purpose the AElement object knows not only the element
 * but also the element's sequence number and the number of the alignment.
 */
class AElement {
	
	public static final int NUM_COLORS = 10;   // foreløpig ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
	
	/**
	 * the element itself.
	 * a node in a DOM tree for the current text.
	 */
	Node element;
	
	/**
	 * the sequence number of the element.
	 * the elements of a text are numbered 0, 1, 2, 3, ...
	 */
	int elementNumber;
	
	/**
	 * the number of the alignment the element is involved in.
	 * alignments have a global numbering 0, 1, 2, 3, ...
	 * #####################################################unused elements under consideration have a special value -1.
	 */
	int alignmentNumber;
	
	/**
	 * the length in characters of the text content of the element. ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤ burde normalisert whitespace
	 */
	int length;
	
	AElement(Node o, int en) {
		element = o;
		elementNumber = en;
		alignmentNumber = -1;   // ¤¤¤ not used yet
		//length = XmlTools.getText(element).length();
		length = element.getTextContent().length();
	}
	
	public Color getColor() {
		//System.out.println("getColor. alignmentNumber = " + alignmentNumber);
		if (alignmentNumber == -1) {
			//return Color.white;
			return Color.getHSBColor((float)0.00, (float)0.00, (float)0.97);
		} else {
			return Color.getHSBColor((float)((float)alignmentNumber / NUM_COLORS), (float)0.13, (float)1.00);
		}
	}
	
	/**
	 * makes a value that keeps - but normalizes - the division into lines of the element.
	 * ¤¤¤because some files can have odd line endings.
	 * if this value is rendered in a list box as a one-line thing it will not wrap.
	 * if this value is rendered in a list box as a multi-line thing it will wrap at line endings.
	 * ¤¤¤ ¤¤¤ ¤¤¤ suddenly wrap works after all! and this method isn't used!
	 * ¤¤¤ fixed
	 */
	public String toString() {
		
		//System.out.println("AElement sin toString");
		// pattern = [\n\r]+ , i.e, matches all kinds of line endings, also multiple endings
		// 2006-09-19 Pattern pattern = Pattern.compile("[\\n\\r]+");
		//Matcher matcher = pattern.matcher(element.toString());   // since 1.5 Node.toString() yields e.g '[s: null]' and not '<s attr="stuff">Blah blah blah</s>'
		//Matcher matcher = pattern.matcher(element.getTextContent());   // ¤¤¤just the text, e.g, 'Blah blah blah'
		//System.out.println("kaller getXmlContent. resultat: " + XmlTools.getXmlContent(element));
		// 2006-09-19 Matcher matcher = pattern.matcher(XmlTools.getXmlContent(element));   // ¤¤¤just the text, e.g, 'Blah blah blah'
		//Matcher matcher = pattern.matcher(element.getNodeValue());   // ¤¤¤leads to Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
		//return "element nummer " + elementNumber + ": " + matcher.replaceAll("\n");   // replaces all kinds of line endings with a standard one
		//return matcher.replaceAll("\n");   // replaces all kinds of line endings with a standard one
		// 2006-09-19 return matcher.replaceAll(" ");   // §§§
		return XmlTools.getXmlContent(element);   // 2006-09-19
		//return "test";   // |||---|||
		// ¤¤¤ merkelig. dersom vi standardiserer til \n,
		// får vi ordentlig wrap + wrap der det er \n.
		// dersom vi setter blank, får vi ikke wrap.
		// ¤¤¤ hmm det er noe tull med \n-måten.
		// når linjer wrapper, ser vi ikke slutten.
		// det er noe feil i utregningen av hvor mye plass som trengs.
		// ¤¤¤ har ikke noe med scroll bar å gjøre. ser det i aligned også.
		//return matcher.replaceAll(" ") + "\n";   // ¤¤¤ yeah! word wrap works! ¤¤¤ øh/ alle elementer blir to linjer høye!
		//return matcher.replaceAll("\n") + "\n";
		
	}
	
	// ###some users might like parent info prepended to the elements
	// in their newline format output files.
	// this method makes a suitable version for that purpose
	public String toNewString(AncestorFilter filter) {
		
		//Pattern pattern = Pattern.compile("[\\n\\r]+");   // pattern = [\n\r]+ , i.e, matches all kinds of line endings, also multiple endings
		//Matcher matcher = pattern.matcher(XmlTools.getXmlContent(element));   // ¤¤¤just the text, e.g, 'Blah blah blah'
		
		//#### skal dette ut i XmlTools?
		
		Node current = element;
		//short test2 = element.getNodeType();   //###debug
		String pathText = "";
		
		if (!filter.denyAll()) {
			
			String ancestorInfo;
			NamedNodeMap attrs;
			String elementName;
			Attr attribute;
			boolean done = false;
			//short test = Node.ELEMENT_NODE;   //###debug
			while (!done) {
				// next parent?
				try {
					current = current.getParentNode();
				} catch (DOMException e) {
					done = true;
				}
				// ???
				if (current == null) {
					done = true;
				} else {
					// but stop before root element is reached
					try {
						Node test = current.getParentNode();
						if (test.getNodeName() == "#document") {
							done = true;
						}
					} catch (DOMException e) {
						done = true;
					}
				}
				if (!done) {
					if (current.getNodeType() == Node.ELEMENT_NODE) {
						//
						elementName = current.getNodeName();
						if (filter.allowElement(elementName)) {
							ancestorInfo = "<" + elementName;
							attrs = current.getAttributes();
							for (int i = 0; i < attrs.getLength(); i++) {
								attribute = (Attr)attrs.item(i);
								if (filter.allowAttribute(elementName, attribute.getName())) {
									ancestorInfo += " " + attribute.getName() + "='" + attribute.getValue() + "'";
								}
							}
							ancestorInfo += ">";
							pathText = ancestorInfo + " " + pathText;
						}
					}
				}
			}
			
		}
		
		//return pathText + matcher.replaceAll(" ");   // §§§
		return pathText + XmlTools.getXmlContent(element);   // 2006-09-19 (nå skal elementet inneholde tekst uten (særlig) unødig whitespace)
		
	}
	
}

/**
 * each Link object represents an alignment - a finished one or pending one.
 * ##########################################################in addition a Link object is used for unused elements under consideration.
 */
class Link {
	
	/**
	 * alignments are numbered 0, 1, 2, 3, ...
	 * the numbering is global, so the numbering of pending alignments
	 * continues the numbering of finished alignments.
	 * #################################################unused elements under consideration have a special number -1. ¤¤¤UNUSED
	 */
	int alignmentNumber;   // ########################skulle hatt set-metode. m.fl.
	
	/**
	 * the numbers of the elements involved in the alignment.
	 * one set for each text.
	 */
	TreeSet[] elementNumbers;
	
	Link() {
		alignmentNumber = -1;   // ¤¤¤
		elementNumbers = new TreeSet[Alignment.NUM_FILES];
		for (int t=0; t<Alignment.NUM_FILES; t++) {
			elementNumbers[t] = new TreeSet<Integer>();   // TreeSet is a SortedSet
		}
	}
	
	TreeSet getElementNumbers(int t) {
		return elementNumbers[t];
	}
	
	boolean empty() {
		for (int t=0; t<Alignment.NUM_FILES; t++) {
			if (elementNumbers[t].size() > 0) {
				return false;
			}
		}
		return true;
	}
	
	int countElements() {
		int count = 0;
		for (int t=0; t<Alignment.NUM_FILES; t++) {
			count += elementNumbers[t].size();
		}
		return count;
	}
	
	public String toString() {
		String str = "(";
		for (int t=0; t<Alignment.NUM_FILES; t++) {
			if (t > 0) { str += ";"; }
			str += "size=" + elementNumbers[t].size();
			Iterator e = ((TreeSet)(elementNumbers[t])).iterator();
			while (e.hasNext()) {
				str += ",el=";
				str += e.next();
			}
		}
		str += ")";
		str += " alignment nummer " + alignmentNumber;
		return str;
	}
	
}

/////////////////////////////////////////////

/**
 * separate thread for loading files.
 * to be more precise it's not for the process of reading a file into a DOM tree
 * but the processing of the elements we do afterwards.
 * but anyway it's a process we want to show progress for in gui components,
 * so we need to have it in a separate thread.
 */
class LoadFileThread extends Thread {
	
	AlignGui gui;
	
	NodeList[] nodes;
	int t;
	
	int percentDone = 0;
	int prevPercentDisplayed = 0;
	int elementNumber;
	
	
	// do GUI updates
	
	void doUpdate(Runnable r) {
		try {
			SwingUtilities.invokeAndWait(r);
		} catch (InvocationTargetException e1) {
			//System.err.println(e1);
			ErrorMessage.error(e1.toString());   // 2006-08-10
		} catch (InterruptedException e2) {
			//System.err.println(e2);
			ErrorMessage.error(e2.toString());   // 2006-08-10
		}
	}
	
	// (we need a constructor with some arguments
	// to get references to the stuff the thread is working with) ¤¤¤
	
	public LoadFileThread(AlignGui gui, NodeList[] nodes, int t) {
		this.gui = gui;
		this.nodes = nodes;
		this.t = t;
	}
	
	// do ¤¤¤
	
	public void run() {
		
		// clear ¤¤¤
		
		doUpdate(new Runnable() {
			public void run() {
				gui.statusLine.setText("");
				gui.statusLine.setProgress(percentDone);
			}
		});
		
		int numElements = nodes[t].getLength();
		// ### i use Math.log(x)/Math.log(10) instead of Math.log10(x) until i've got java 1.5 installed
		int step = Math.round((float)(Math.pow(10, Math.sqrt((((Math.log((double)numElements / 100) / Math.log(10))) + 1)))));
		step = Math.min(step, 100);
		step = Math.max(step, 10);
		
		for (elementNumber = 0; elementNumber < numElements; elementNumber++) {
			
			AElement element = new AElement(nodes[t].item(elementNumber), elementNumber);
			gui.model.unaligned.add(t, element);
			
			percentDone = Math.round((float)((float)(elementNumber+1) / numElements * 100.0));
			
			//if ((elementNumber + 1) % 100 == 0) {
			if (percentDone >= prevPercentDisplayed + step) {
				
				doUpdate(new Runnable() {
					public void run() {
						gui.statusLine.setText(Integer.toString(elementNumber+1));
						gui.statusLine.setProgress(percentDone);
					}
				});
				
				prevPercentDisplayed = percentDone;
				
			}
			
		}
		
		// ¤¤¤
		
		doUpdate(new Runnable() {
			public void run() {
				//gui.statusLine.setText("Finished");
				gui.statusLine.setText("Text parsed");
				gui.statusLine.setProgress(100);
			}
		});
		
		// ¤¤¤problem: sometimes at this point the content of the unaligned area doesn't show.
		// why?
		// shake it by removing and adding first element.
		// ¤¤¤doesn't always help!?
		// 2006-09-19: worse with JTextArea than JLabel?
		gui.model.unaligned.elements[t].add(0 ,gui.model.unaligned.elements[t].remove(0));
		
	}
	
}

/////////////////////////////////////////////

/**
 * information about how the current elements under alignment match
 * with respect to anchor words, proper names, dice, length, etc.
 * displayable version.
 * formatted into a list of lines
 * ######### to ulike steder som beregner skåre
 */
//class MatchInfoDisplayable {
class MatchInfo {
	
	AlignmentModel model;
	protected DefaultListModel displayableList;
	
	//MatchInfoDisplayable(AlignmentModel model) {
	MatchInfo(AlignmentModel model) {
		
		this.model = model;
		displayableList = new DefaultListModel();
		
	}
	
	//public void compute() {
	//
	//	//...;
	//
	//}
	
	public void clear() {   // §§§§§§§§§§§§§§§§§§§§§§§
		
		//...;
		
	}
	
	public void purge() {
		displayableList.clear();
		// (keep model)
	}
	
	//public String toString() {
	public void computeDisplayableList() {
		
		int t;
		int n;
		
		//System.out.println("computeDisplayableList()");
		
		ElementInfoToBeCompared elementInfoToBeCompared = new ElementInfoToBeCompared(model);
		
		// collect necessary info in an ElementInfoToBeCompared object
		
		for (t=0; t<Alignment.NUM_FILES; t++) {
			for (Enumeration en = model.toAlign.elements[t].elements(); en.hasMoreElements();) {
				n = ((AElement)en.nextElement()).elementNumber;
				try {
					ElementInfo info = model.compare.elementsInfo[t].getElementInfo(model, n, t);
					elementInfoToBeCompared.add(info, t);
				} catch (EndOfTextException e) {   // skal ikke forekomme her?
					System.out.println("*** program error.unexpected EndOfTextException ***");
					//return "*** program error. unexpected EndOfTextException ***";
					displayableList.add(0, (Object)"*** Program error. Unexpected EndOfTextException ***");
				}
			}
		}
		
		// get displayable info about how they match
		
		//return elementInfoToBeCompared.toString();
		List list = elementInfoToBeCompared.toList();
		Iterator it = list.iterator();
		displayableList.clear();
		while (it.hasNext()) {
			displayableList.addElement(it.next());
		}
		
	}
	
}

/**
 * some elements are to be compared,
 * either the elements in a step to be tried out,
 * or the elements under alignment, visible in the gui.
 * this class contains (refers to) the elements to be compared.
 * to be more precise the elements refered to
 * are ElementInfo objects in the Compare object.
 * methods that need to know the score of the element comparison,
 * or details about how they match,
 * must establish an ElementInfoToBeCompared object,
 * and call the object's getScore or toString method
 */
class ElementInfoToBeCompared {
	
	private AlignmentModel model;
	
	public static final String INDENT = "  ";
	
	List[] info = new ArrayList[Alignment.NUM_FILES];   // lists of ElementInfo, one for each text
	
	// score and details. calculated on demand, and only once
	
	//// -1 = not calculated yet
	//private float score = -1.0f;   // set by toString(). ### i mean toList(). can be got by getScore(). see getScore() §§§§§§§§§§§§§§§§§§§§
	// not calculated yet // 2006-09-20
	private float score = AlignmentModel.ELEMENTINFO_SCORE_NOT_CALCULATED;   // set by toList(). can be got by getScore(). see getScore() §§§§§§§§§§§§§§§§§§§§ // 2006-09-20
	//StringBuffer str = new StringBuffer();
	List<String> ret = new ArrayList<String>();
	
	public ElementInfoToBeCompared(AlignmentModel model) {
		
		this.model = model;
		
		for (int t=0; t<Alignment.NUM_FILES; t++) {
			info[t] = new ArrayList<ElementInfo>();
		}
		
	}
	
	public void add(ElementInfo elementInfo, int t) {
		
		//££££SKAL DENNE METODEN SØRGE FOR AT INFORMASJONEN OM ORD-POSISJON BLIR "GLOBAL",
		//OG IKKE LOKAL FOR HVERT ELEMENT?
		//nei, det går vel ikke, for denne klassen eier ikke elementene
		
		//System.out.println("&&& enter ElementInfoToBeCompared.add(...)");
		info[t].add(elementInfo);
		//System.out.println("t=" + t + ", after add: info[t].size()=" + info[t].size());
		
	}
	
	public boolean empty() {
		
		//System.out.println("&&& enter ElementInfoToBeCompared.empty()");
		for (int t=0; t<Alignment.NUM_FILES; t++) {
			//System.out.println("t=" + t + ", info[t].size()=" + info[t].size());
			if (info[t].size() == 0) {
				//System.out.println("return true");
				return true;
			}
		}
		//System.out.println("return false");
		return false;
		
	}
	
	public float getScore() {
		
		//System.out.println("&&& enter ElementInfoToBeCompared.getScore(). score = " + score);
		//if (score <= -0.9f) {   // i.e, == -1.0f, i.e, not calculated
		if (score == AlignmentModel.ELEMENTINFO_SCORE_NOT_CALCULATED) {   // 2006-09-20. this change corrects an error. earlier element-info with calculated score -99999.0 (menat to kill path) would get their score re-calculated
			//System.out.println("må beregne");
			// not been calculated yet.
			// use toString() to calculate it
			// and just throw away the String returned
			//toString();
			toList();
		} else {
			//System.out.println("ikke nødvendig å beregne");
		}
		//System.out.println("&&& leave ElementInfoToBeCompared.getScore(). score = " + score);
		return score;
		
	}
	
	//public String toString() {
	public List toList() {
		
		//System.out.print("entering toList(). score=" + score + ". ");
		
		DecimalFormat myFormatter = (DecimalFormat)NumberFormat.getInstance(Locale.ENGLISH);
		myFormatter.applyPattern("0.###");
		
		//System.out.println("enter ElementInfoToBeCompared.toList() ============================");
		//System.out.println("&&& enter ElementInfoToBeCompared.toString(). score = " + score);
		//if (score <= -0.9f) {   // i.e, == -1.0f, i.e, not calculated
		if (score == AlignmentModel.ELEMENTINFO_SCORE_NOT_CALCULATED) {   // 2006-09-20. this change corrects an error. earlier element-info with calculated score -99999.0 (menat to kill path) would get their score re-calculated
			// not been calculated yet.
			//System.out.print("score not calculated yet. ");
			
			score = 0.0f;
			//System.out.println("&&& ElementInfoToBeCompared.toString(). init score = " + score);
			
			if (empty()) {
				
				// keep score 0. keep str ""
				//System.out.println("&&& ElementInfoToBeCompared.toString(). empty. keep score 0. keep str null string");
				
			} else {
				
				int t;
				int tt;
				Iterator it;
				Iterator it1;
				Iterator it2;
				String retLine;   // to contain one line of info
				
				// 2006-04-05
				
				// 2006-09-20
				//////////////////
				// bad lengths? //
				//////////////////
				
				int[] length = new int[Alignment.NUM_FILES];   // length in chars of the relevant elements of each text
				int[] elementCount = new int[Alignment.NUM_FILES];   // number of relevant elements from each text
				
				for (t=0; t<Alignment.NUM_FILES; t++) {
					length[t] = 0;
					it = info[t].iterator();
					while (it.hasNext()) {
						ElementInfo info1 = (ElementInfo)it.next();
						length[t] += info1.length;
					}
					elementCount[t] = info[t].size();
				}
				
				if (SimilarityUtils.badLengthCorrelation(length[0], length[1], elementCount[0], elementCount[1], model.getLengthRatio())) {
					
					score = AlignmentModel.ELEMENTINFO_SCORE_HOPELESS;
					retLine = "Very poor length match";
					ret.add(retLine);
					
				} else {
					// end 2006-09-20
					
					// this methods produces detailed information about how the
					// elements under consideration match,
					// and collects that information in List ret
					
					// do reporting###
					
					// we don't know the common score for the word based methods yet.
					// we'll have to come back and insert the score later.
					retLine = INDENT + "Word based methods score: ";
					ret.add(retLine);
					// remember in which line to insert the score later
					int wordMethodsScoreLineNumber = ret.size() - 1;
					
					// end 2006-04-05
					
					//////////////////
					// anchor words //
					//////////////////
					
					Clusters anchorWordClusters = new Clusters();   // 2006-04-05
					
					//int anchorWordScore = 0;   // 2006-04-05
					
					AnchorWordHit hit;
					int index;
					//int high = 0;   // 2006-04-05
					//int highSum = 0;   // 2006-04-05
					//int low = 0;   // 2006-04-05
					//int lowSum = 0;   // 2006-04-05
					//int oneSum = 0;   // 2006-04-05
					int count;
					int smallest;
					int smallestCount;
					boolean presentInAllTexts;
					int matchType;
					float weight;
					String word;
					int pos;
					int len;
					int elementNumber ;
					int indentLevel;
					boolean includeMatchType;
					
					//System.out.println("&&& ElementInfoToBeCompared.toString(). not empty. go ahead and calculate");
					// for each text t make a list hits[t] of anchor word hits
					// for (from) the elements under consideration from text t.
					// a hit is an occurrence of an anchor word in a text,
					// but not yet a confirmed match with a word from the other text ###
					
					List<AnchorWordHit>[] hits = new ArrayList[Alignment.NUM_FILES];
					for (t=0; t<Alignment.NUM_FILES; t++) {
						hits[t] = new ArrayList<AnchorWordHit>();
						//System.out.println("før anchor words sin 'it = info[t].iterator();'");
						it = info[t].iterator();   //£££ER DETTE LØKKE OVER ELEMENTER?
						//System.out.println("etter anchor words sin 'it = info[t].iterator();'");
						///////////////@@@@@@@@@@int offset = 0;   // 2006-04-05
						while (it.hasNext()) {
							ElementInfo info1 = (ElementInfo)it.next();
							it2 = info1.anchorWordHits.hits.iterator();
							while (it2.hasNext()) {
								hit = (AnchorWordHit)it2.next();   //£££DA HAR hit HER EN NUMMERERING AV ORDPOSISJON SOM ER LOKAL FOR HVERT ELEMENT.
								//System.out.println("adder " + hit + " for tekst nr " + t);
								// change word position from local within each element
								// to global within all the elements under consideration for text t.
								// ####alternativ: operere med to-nivå nummerering i hit-ene etc:
								// 1 elementnummer, 2 lokalt ordnummer
								//System.out.println("hit.getPos() før   = " + hit.getPos());
								///////////////@@@@@@@@@@if (offset != 0) { hit.setPos(hit.getPos() + offset); }   // 2006-04-05
								//System.out.println("hit.getPos() etter = " + hit.getPos());
								hits[t].add(hit);
							}
							///////////////@@@@@@@@@@offset += info1.words.length;   // 2006-04-05
						}
						//System.out.println("hits[" + t + "] = " + hits[t]);
					}
					
					// see if any hits match up,
					// i.e, if any occurring anchor words in different texts
					// share the same anchor word list entry
					
					// sort these lists of hits on
					// (1) index (anchor word list entry number) and
					// (2) word
					for (t=0; t<Alignment.NUM_FILES; t++) {
						Collections.sort(hits[t], new AnchorWordHitComparator());
						//System.out.println("sortert hits[" + t + "] = " + hits[t]);
					}
					
					// match up hits.
					// first init pointers to current hit in each list
					int[] current = new int[Alignment.NUM_FILES];
					for (t=0; t<Alignment.NUM_FILES; t++) {
						current[t] = 0;
						//System.out.println("pointer to current hit in text " + t + " = " + current[t]);
					}
					
					// then do stuff.
					// one loop pass per anchor word list entry with hits.
					// do them in index order, smallest first
					// (we just had them sorted just for this reason)
					boolean done = false;
					while (!done) {
						//System.out.println("next pass while (!done)");
						// find smallest anchor word list index in remaining hits.
						// check if it is present inn all texts
						smallest = Integer.MAX_VALUE;
						smallestCount = 0;
						//System.out.println("smallest anchor word list index so far = " + smallest);
						for (t=0; t<Alignment.NUM_FILES; t++) {
							//System.out.println("next text - " + t);
							if (current[t] < hits[t].size()) {
								//System.out.println("there are remaining hits for text " + t);
								// there are remaining hits for text t
								hit = (AnchorWordHit)((List)hits[t]).get(current[t]);   // ### (AnchorWordHit)
								if (hit.getIndex().intValue() < smallest) {
									// found a new smallest
									smallest = hit.getIndex().intValue();
									// reset count
									smallestCount = 1;
									//System.out.println("found a smaller one: " + smallest);
								} else if (hit.getIndex().intValue() == smallest) {
									// same smallest. increment count
									smallestCount++;
								} // else not a smallest
							}
						}
						presentInAllTexts = (smallestCount == Alignment.NUM_FILES);
						/*if (presentInAllTexts) {
						 //System.out.println("hit for index " + smallest + " present in all texts");
						  } else {
						  //System.out.println("hit for index " + smallest + " not present in all texts");
						   }*/
						// in the following: collect data for output only if the hit### was present in all texts
						// ...
						if (smallest == Integer.MAX_VALUE) {
							//System.out.println("found no remaining hits, for any text");
							// no remaining hits, for any text
							done = true;
						} else {
							// there are remaining hits, at least for some of the texts.
							// find all hits with this smallest remaining anchor word list index.
							//// look through all texts and find the highest/lowest number of hits in any text.
							//// this highest/lowest number might be the contribution to the anchor word score
							//// for this anchor word list entry,
							//// provided the hit is in every text..
							//// example:
							//// anchor word entry: "xxx/yyy,yy"
							//// texts: "I saw a xxx going down the xxx" vs "Jeg så en yyy nede i yy yyy"
							//// (§§§§§§§§§idiotisk, ubrukelig eksempel)
							//// 2 hits in first text and 3 hits in second text makes a score of max(2,3) = 3
							//System.out.println("there are remaining hits, at least for some of the texts");
							//high = 0;
							//low = Integer.MAX_VALUE;
							//System.out.println("highest number of hits in any text so far = " + high);
							//System.out.println("lowest number of hits in any text so far = " + low);
							//retLine = "";   // init next line of info // 2006-04-05
							for (t=0; t<Alignment.NUM_FILES; t++) {
								//System.out.println("next text - " + t);
								/*if (presentInAllTexts) {
								 if (t == 0) {
								 // ¤¤¤ hvorfor brukes ikke toString-metode? duplisert programmering
								  //str.append(INDENT + INDENT + (smallest + 1) + " ");   // +1 since we want anchor word entries numbered from 1 and upwards when they are displayed
								   retLine += INDENT + INDENT + (smallest + 1) + " ";   // +1 since we want anchor word entries numbered from 1 and upwards when they are displayed
								   } else {
								   //str.append("/");
								    retLine += "/";
								    }
								    }*/ // 2006-04-05
								count = 0;
								//System.out.println("number of hits in current text so far = " + count);
								boolean first = true;
								if (current[t] < hits[t].size()) {
									//System.out.println("there are remaining hits, at least for text " + t);
									// there are remaining hits for text t.
									// get all hits with smallest index, if any
									boolean done2 = false;
									for (int c = current[t]; !done2; c++) {
										//System.out.println("check hit " + c + " for text " + t);
										hit = (AnchorWordHit)hits[t].get(c);
										//System.out.println("hit = " + hit);
										index = hit.getIndex().intValue();
										if (index == smallest) {
											//System.out.println("this is a smallest hit");
											//// samle opp ordet i en ### liste for text t, såsant det er et nytt ord
											// add to cluster list   // 2006-04-05
											elementNumber = hit.getElementNumber();   // 2006-04-05
											pos = hit.getPos();   // 2006-04-05
											//System.out.println("t = " + t + ", elementNumber = " + elementNumber + ", pos = " + pos);
											word = hit.getWord();   // 2006-04-05
											len = Utils.countWords(word);   // 2006-04-07 ### hadde vært penere med egen member len i tillegg til pos, slik som Ref har
											matchType = index;   // each anchor word entry is its own match type, sort of   // 2006-04-05
											//weight = 1.0f;   // 2006-04-05
											if (len > 1) {   // (2006-04-10)
												weight = model.getAnchorPhraseMatchWeight();   // 2006-04-07
											} else {   // (2006-04-10)
												weight = model.getAnchorWordMatchWeight();   // 2006-04-07
											}   // (2006-04-10)
											//Ref ref = new Ref(matchType, weight, t, elementNumber, pos, word);   // 2006-04-05
											//System.out.println("legger ny ref i anchorWordClusters: " + ref);
											//anchorWordClusters.add(ref);   // ### heller en addRef-metode? handler om grenseoppgang mellom clustergreiene og utsiden   // 2006-04-05
											// ### 2006-04-06 her er det et problem. vi adder én ref om gangen, og de samler seg ikke i cluster.
											// må enten adde dem i par eller hele clustre, eller må vi ha en annen addemetode for anker-ref,
											// slik at ref-ene havner i samme cluster når de har samme ankerordentry.
											// eller den addemetoden som vi har må behandle anker-ref annerledes.
											// matchemetoden, mener jeg!
											// ja - gjør det siste. lar matchemetoden behandle anker-ref annerledes.
											// forresten. får da inn ref-er som ikke har med matching å gjøre,
											// f.eks to forekomster av samme ankerord i samme tekst.
											// og uansett får jeg jo også cluster med enslig ref, når jeg gir én ref om gangen.
											// aha. disse siste problemene har jo med hva jeg gjør her.
											// addingen skal behandle anker-ref annerledes,
											// men her i denne metoden må jeg adde ref bare når presentInAllTexts
											//anchorWordClusters.add(new Ref(matchType, weight, t, elementNumber, pos, word));   // ### heller en addRef-metode? handler om grenseoppgang mellom clustergreiene og utsiden   // 2006-04-05
											// ...
											/*if (first) {
											 first = false;
											 } else {
											 if (presentInAllTexts) {
											 //str.append(",");
											  retLine += ",";
											  }
											  }*/
											if (presentInAllTexts) {
												//str.append(hit.getWord());
												//retLine += hit.getWord();
												//anchorWordClusters.add(new Ref(matchType, weight, t, elementNumber, pos, word));   // ### heller en addRef-metode? handler om grenseoppgang mellom clustergreiene og utsiden   // 2006-04-06
												anchorWordClusters.add(new Ref(matchType, weight, t, elementNumber, pos, len, word));   // ### heller en addRef-metode? handler om grenseoppgang mellom clustergreiene og utsiden   // 2006-04-07
											}
											count++;
											//System.out.println("number of hits in current text so far = " + count);
										} else {
											done2 = true;
										}
										if (c+1 >= hits[t].size()) {
											done2 = true;
										}
									}
									// ...
									current[t] += count;
									//System.out.println("updated pointer to current hit in text " + t + " to " + current[t]);
								}
								//if (count > high) { high = count; }
								//if (count < low) { low = count; }
							}
							/*if (presentInAllTexts) {
							 if (model.getClusterScoreMethod() == 3) {
							 //str.append(" (" + high + " points)");
							  retLine += " (" + high + " points)";
							  } if (model.getClusterScoreMethod() == 2) {
							  //str.append(" (" + low + " points)");
							   retLine += " (" + low + " points)";
							   } else { // if (model.getClusterScoreMethod() == 1)
							   //str.append(" (" + 1 + " points)");   // ¤¤¤ sløyfe?
							    retLine += " (" + 1 + " points)";   // ¤¤¤ sløyfe?
							    }
							    //str.append("\n");
							     //System.out.println("add'er retLine = " + retLine);
							      ret.add(retLine);
							      //System.out.println("nå er det " + ret.size() + " linjer i ret");
							       }*/ // 2006-04-05
						}
						/*// add points for this anchor word list entry
						 if (presentInAllTexts) {
						 highSum += high;
						 lowSum += low;
						 oneSum += 1;
						 }*/ // 2006-04-05
					}
					
					// ...
					/*if (model.getClusterScoreMethod() == 3) {
					 anchorWordScore = highSum;
					 } if (model.getClusterScoreMethod() == 2) {
					 anchorWordScore = lowSum;
					 } else { // if (model.getClusterScoreMethod() == 1)
					 anchorWordScore = oneSum;
					 }*/ // 2006-04-05
					
					//System.out.println(">>> anchorWordScore = " + anchorWordScore + "\n");
					
					// ...
					//if (str.length() > 0) {
					//	str.insert(0, INDENT + "Anchor word score: " + anchorWordScore + "\n");
					//} else {
					//	str.insert(0, INDENT + "No anchor word matches. Score: 0\n");
					//}
					
					//int anchorWordScore = anchorWordClusters.getScore(model.getClusterScoreMethod());   // 2006-04-05
					//float anchorWordScore = anchorWordClusters.getScore(model.getClusterScoreMethod());   // 2006-04-07
					float anchorWordScore = anchorWordClusters.getScore(model.getLargeClusterScorePercentage());
					
					// next line of info...
					//if (anchorWordScore > 0) {   // ### ryddigere med samme syntaks alltid
					retLine = INDENT + INDENT + "Anchor word score: " + myFormatter.format(anchorWordScore);   // 2006-04-05
					//} else {
					//	retLine = INDENT + "No anchor word matches. Score: 0";
					//}
					//// ...is header for anchor info. insert at top
					//ret.add(0, retLine);
					ret.add(retLine);   // 2006-04-05
					
					indentLevel = 3;   // 2006-04-05
					includeMatchType = true;   // i.e, include anchor word entry number. ### + 1 ### ugly
					ret.addAll(anchorWordClusters.getDetails(indentLevel, includeMatchType));   // getDetails() does its own indentation and endline. ### ikke helt bra?   // 2006-04-05
					
					//// ...
					//score += anchorWordScore;   // 2006-04-05
					
					///////////////////
					// proper names, //
					// dice,         //
					// and numbers   //
					///////////////////
					
					//int properNameScore = 0;   // 2006-04-05
					//int diceScore = 0;   // 2006-04-05
					
					// check all the words in one text against all the words in the other.
					// collect clusters of proper names.
					// collect clusters of dice-related words.
					// collect clusters of numbers.
					// (usually all the words in a cluster will be related to each other,
					// but not necessarily.)
					String word1;
					String word2;
					String nextWord1;   // 2006-04-07
					String nextWord2;   // 2006-04-07
					//String phrase1;   // 2006-04-07. words glued together without space between them
					//String phrase2;   // 2006-04-07. words glued together without space between them
					String showPhrase1;   // 2006-04-18. words with space between them
					String showPhrase2;   // 2006-04-18. words with space between them
					//Clusters properNameClusters = new Clusters(model.getClusterScoreMethod());
					Clusters properNameClusters = new Clusters();   // 2006-04-05
					//Clusters diceClusters = new Clusters(model.getClusterScoreMethod());
					Clusters diceClusters = new Clusters();   // 2006-04-05
					Clusters numberClusters = new Clusters();   // 2006-04-06
					//System.out.println("Skipper proper, dice, numbers");
					for (t=0; t<Alignment.NUM_FILES; t++) {
						for (tt=t+1; tt<Alignment.NUM_FILES; tt++) {
							//System.out.println("\nneste (eneste) gjennomløp. t = " + t + ". tt = " + tt);
							
							// check text t against text tt
							// (in practice text 0 (known to the user as text 1)
							// against text 1 (known to the user as text 2))
							
							// ... each word in relevant elements of text t
							
							it1 = info[t].iterator();
							while (it1.hasNext()) {
								ElementInfo info1 = (ElementInfo)it1.next();
								for (int x = 0; x < info1.words.length; x++) {
									
									word1 = info1.words[x];
									// 2006-04-07
									if (x < info1.words.length - 1) {
										nextWord1 = info1.words[x+1];
									} else {
										nextWord1 = "";
									}
									
									// end 2006-04-07
									// ... each word in relevant elements of text tt
									
									it2 = info[tt].iterator();
									while (it2.hasNext()) {
										ElementInfo info2 = (ElementInfo)it2.next();
										for (int y = 0; y < info2.words.length; y++) {
											
											word2 = info2.words[y];
											// 2006-04-07
											if (y < info2.words.length - 1) {
												nextWord2 = info2.words[y+1];
											} else {
												nextWord2 = "";
											}
											// end 2006-04-07
											
											// compare two words
											
											// proper names
											
											if (Character.isUpperCase(word1.charAt(0)) && Character.isUpperCase(word2.charAt(0)) && word1.equals(word2)) {
												
												// the words are capitalized and equal.
												// add to cluster list
												
												//System.out.println("\n" + word1 + " and " + word2 + " are capitalized and equal. add to cluster list");
												//properNameClusters.add(t, tt, x, y);
												//properNameClusters.add(t, tt, x, y, word1, word2);
												matchType = Match.PROPER;   // 2006-04-05
												//weight = 1.0f;   // 2006-04-05
												weight = model.getProperNameMatchWeight();   // 2006-04-07
												//properNameClusters.add(matchType, weight, t, tt, info1.elementNumber, info2.elementNumber, x, y, word1, word2);   // 2006-04-05
												properNameClusters.add(matchType, weight, t, tt, info1.elementNumber, info2.elementNumber, x, y, 1, 1, word1, word2);   // 2006-04-07
												//System.out.println("%%% properNameClusters etter add = " + properNameClusters);
												
											}
											
											// dice
											
											// first check if the words are long enough to be considered
											if ((word1.length() >= model.getDiceMinWordLength()) && (word2.length() >= model.getDiceMinWordLength())) {
												
												//System.out.println("\nskal dice-sammenlikne " + word1 + " med " + word2);
												//if (SimilarityUtils.dice(word1, word2) >= model.getDiceMinCountingScore()) {
												if (SimilarityUtils.diceMatch(word1, word2, model.getDiceMinCountingScore())) {   // 2006-08-09
													
													// the words are related.
													// add to cluster list
													
													//System.out.println("\n" + word1 + " and " + word2 + " are dice-related. add to cluster list");
													//diceClusters.add(t, tt, x, y, word1, word2);
													matchType = Match.DICE;   // 2006-04-05
													//weight = 1.0f;   // 2006-04-05
													weight = model.getDiceMatchWeight();   // 2006-04-07
													//diceClusters.add(matchType, weight, t, tt, info1.elementNumber, info2.elementNumber, x, y, word1, word2);   // 2006-04-05
													diceClusters.add(matchType, weight, t, tt, info1.elementNumber, info2.elementNumber, x, y, 1, 1, word1, word2);   // 2006-04-07
													
												}
												
											}
											
											// 2006-04-07
											
											// also try dice on 2 words against 1 word...
											
											if (nextWord1 != "") {
												
												//phrase1 = word1 + " " + nextWord1;
												//phrase1 = word1 + nextWord1;
												showPhrase1 = word1 + " " + nextWord1;   // 2006-04-18
												
												// first check if the phrases/words are long enough to be considered
												//if ((phrase1.length()-1 >= model.getDiceMinWordLength()) && (word2.length() >= model.getDiceMinWordLength())) {
												//if ((phrase1.length() >= model.getDiceMinWordLength()) && (word2.length() >= model.getDiceMinWordLength())) {
												if (   (word1.length()     >= model.getDiceMinWordLength())
														&& (nextWord1.length() >= model.getDiceMinWordLength())
														&& (word2.length()     >= model.getDiceMinWordLength())) {   // 2006-04-18
													
													//if (SimilarityUtils.dice(phrase1, word2) >= model.getDiceMinCountingScore()) {
													//if (SimilarityUtils.dice(phrase1, word2, "2-1") >= model.getDiceMinCountingScore()) {   // 2006-04-18
													if (SimilarityUtils.diceMatch(word1, nextWord1, word2, "2-1", model.getDiceMinCountingScore())) {   // 2006-08-09
														
														// the phrases/words are related.
														// add to cluster list
														
														//System.out.println("\n" + phrase1 + " and " + word2 + " are dice-related. add to cluster list");
														matchType = Match.DICE;   // 2006-04-05
														weight = model.getDicePhraseMatchWeight();   // 2006-04-07
														//diceClusters.add(matchType, weight, t, tt, info1.elementNumber, info2.elementNumber, x, y, 2, 1, phrase1, word2);
														diceClusters.add(matchType, weight, t, tt, info1.elementNumber, info2.elementNumber, x, y, 2, 1, showPhrase1, word2);   // 2006-04-18
														
													}
													
												}
												
											}
											
											// ...and 1 word against 2 words
											
											if (nextWord2 != "") {
												
												//phrase2 = word2 + " " + nextWord2;
												//phrase2 = word2 + nextWord2;
												showPhrase2 = word2 + " " + nextWord2;   // 2006-04-18
												
												// first check if the phrases/words are long enough to be considered
												//if ((word1.length() >= model.getDiceMinWordLength()) && (phrase2.length()-1 >= model.getDiceMinWordLength())) {
												//if ((word1.length() >= model.getDiceMinWordLength()) && (phrase2.length() >= model.getDiceMinWordLength())) {
												if (   (word1.length()     >= model.getDiceMinWordLength())
														&& (word2.length()     >= model.getDiceMinWordLength())
														&& (nextWord2.length() >= model.getDiceMinWordLength())) {   // 2006-04-18
													
													//if (SimilarityUtils.dice(word1, phrase2) >= model.getDiceMinCountingScore()) {
													//if (SimilarityUtils.dice(word1, phrase2, "1-2") >= model.getDiceMinCountingScore()) {   // 2006-04-18
													if (SimilarityUtils.diceMatch(word1, word2, nextWord2, "1-2", model.getDiceMinCountingScore())) {   // 2006-08-09
														
														// the phrases/words are related.
														// add to cluster list
														
														//System.out.println("\n" + word1 + " and " + phrase2 + " are dice-related. add to cluster list");
														matchType = Match.DICE;   // 2006-04-05
														weight = model.getDicePhraseMatchWeight();   // 2006-04-07
														//diceClusters.add(matchType, weight, t, tt, info1.elementNumber, info2.elementNumber, x, y, 1, 2, word1, phrase2);
														diceClusters.add(matchType, weight, t, tt, info1.elementNumber, info2.elementNumber, x, y, 1, 2, word1, showPhrase2);   // 2006-04-18
														
													}
													
												}
												
											}
											
											// end 2006-04-07
											
											// 2006-04-06
											
											// numbers
											
											float num1;
											float num2;
											try {
												num1 = Float.parseFloat(word1);
												num2 = Float.parseFloat(word2);
												if (num1 == num2) {
													
													// same number
													// add to cluster list
													
													matchType = Match.NUMBER;
													//weight = 1.0f;
													weight = model.getNumberMatchWeight();   // 2006-04-07
													//numberClusters.add(matchType, weight, t, tt, info1.elementNumber, info2.elementNumber, x, y, word1, word2);
													numberClusters.add(matchType, weight, t, tt, info1.elementNumber, info2.elementNumber, x, y, 1, 1, word1, word2);   // 2006-04-07
													
												}
												
											} catch (NumberFormatException ne) {
											}
											
											// end 2006-04-06
											
										}
									}
									
								}
								
							}
						}
					}
					
					//System.out.println("%%% properNameClusters ferdig = " + properNameClusters);
					
					//int properNameScore = properNameClusters.getScore(model.getClusterScoreMethod());
					//float properNameScore = properNameClusters.getScore(model.getClusterScoreMethod());
					float properNameScore = properNameClusters.getScore(model.getLargeClusterScorePercentage());
					
					//int diceScore = diceClusters.getScore(model.getClusterScoreMethod());
					//float diceScore = diceClusters.getScore(model.getClusterScoreMethod());
					float diceScore = diceClusters.getScore(model.getLargeClusterScorePercentage());
					
					//int numberScore = numberClusters.getScore(model.getClusterScoreMethod());
					//float numberScore = numberClusters.getScore(model.getClusterScoreMethod());
					float numberScore = numberClusters.getScore(model.getLargeClusterScorePercentage());
					
					// ...
					
					//str.append(INDENT + "Proper name score: " + properNameScore + "\n");
					retLine = INDENT + INDENT + "Proper name score: " + myFormatter.format(properNameScore);   // 2006-04-05
					ret.add(retLine);
					
					//score += properNameScore;   // 2006-04-05
					
					//str.append(properNameClusters.getWords());   // getWords() does its own indentation and endline. ### ikke helt bra?
					//ret.addAll(properNameClusters.getDetails());   // getDetails() does its own indentation and endline. ### ikke helt bra?
					indentLevel = 3;   // 2006-04-05
					includeMatchType = false;
					ret.addAll(properNameClusters.getDetails(indentLevel, includeMatchType));   // getDetails() does its own indentation and endline. ### ikke helt bra?   // 2006-04-05
					
					//str.append(INDENT + "Dice score: " + diceScore + "\n");
					retLine = INDENT + INDENT + "Dice score: " + myFormatter.format(diceScore);   // 2006-04-05
					ret.add(retLine);
					
					//score += diceScore;   // 2006-04-05
					
					//str.append(diceClusters.getWords());   // getWords() does its own indentation and endline. ### ikke helt bra?
					//ret.addAll(diceClusters.getDetails());   // getDetails() does its own indentation and endline. ### ikke helt bra?
					indentLevel = 3;   // 2006-04-05
					includeMatchType = false;
					ret.addAll(diceClusters.getDetails(indentLevel, includeMatchType));   // getDetails() does its own indentation and endline. ### ikke helt bra?   // 2006-04-05
					
					// 2006-04-06
					
					retLine = INDENT + INDENT + "Number score: " + myFormatter.format(numberScore);
					ret.add(retLine);
					
					indentLevel = 3;
					includeMatchType = false;
					ret.addAll(numberClusters.getDetails(indentLevel, includeMatchType));
					
					// end 2006-04-06
					
					// 2006-04-05
					
					////////////////////////////////
					
					// common score for anchor words, proper names, dice and numbers
					
					Clusters commonClusters = new Clusters();
					commonClusters.add(anchorWordClusters);
					commonClusters.add(properNameClusters);
					commonClusters.add(diceClusters);
					commonClusters.add(numberClusters);   // 2006-04-06
					
					//int commonScore = commonClusters.getScore(model.getClusterScoreMethod());
					//float commonScore = commonClusters.getScore(model.getClusterScoreMethod());
					float commonScore = commonClusters.getScore(model.getLargeClusterScorePercentage());
					
					// go back and insert the common score for the word based methods
					ret.set(wordMethodsScoreLineNumber, (String)ret.get(wordMethodsScoreLineNumber) + myFormatter.format(commonScore));
					
					score += commonScore;
					
					// end 2006-04-05
					
					// debugging or testing
					String tempo = commonClusters.nonTrivialClusters_ToString();
					if (tempo != "") {
						System.out.println(tempo);
					}
					
					////////////////////////////////
					// scoring special characters //
					////////////////////////////////
					
					//int scoringCharacterScore = 0;   // 2006-04-05
					
					// check all the ... ... ...
					
					String char1;
					String char2;
					//Clusters scoringCharacterClusters = new Clusters(model.getClusterScoreMethod());
					Clusters scoringCharacterClusters = new Clusters();   // 2006-04-05
					for (t=0; t<Alignment.NUM_FILES; t++) {
						for (tt=t+1; tt<Alignment.NUM_FILES; tt++) {
							
							// check text t against text tt (in practice 0 (1) against 1 (2))
							
							// each scoring special character in relevant elements of text t
							
							it1 = info[t].iterator();
							while (it1.hasNext()) {
								ElementInfo info1 = (ElementInfo)it1.next();
								//System.out.println("info1.scoringCharacters = " + info1.scoringCharacters);
								for (int x = 0; x < info1.scoringCharacters.length(); x++) {
									char1 = info1.scoringCharacters.substring(x, x+1);
									
									// ... each scoring char in relevant elements of text tt
									
									it2 = info[tt].iterator();
									while (it2.hasNext()) {
										ElementInfo info2 = (ElementInfo)it2.next();
										//System.out.println("info2.scoringCharacters = " + info2.scoringCharacters);
										for (int y = 0; y < info2.scoringCharacters.length(); y++) {
											char2 = info2.scoringCharacters.substring(y, y+1);
											
											// compare two characters
											
											if (char1.equals(char2)) {
												
												// equal.
												// add to cluster list
												
												//scoringCharacterClusters.add(t, tt, x, y, char1, char2);
												matchType = Match.SCORING_CHARACTERS;   // ### irrelevant   // 2006-04-05
												//weight = 1.0f;   // 2006-04-05
												weight = model.getScoringCharacterMatchWeight();   // 2006-04-07
												//scoringCharacterClusters.add(matchType, weight, t, tt, info1.elementNumber, info2.elementNumber, x, y, char1, char2);   // 2006-04-05
												scoringCharacterClusters.add(matchType, weight, t, tt, info1.elementNumber, info2.elementNumber, x, y, 1, 1, char1, char2);   // 2006-04-07
												
											}
											
										}
									}
									
								}
								
							}
							
						}
					}
					
					//int scoringCharacterScore = scoringCharacterClusters.getScore(model.getClusterScoreMethod());
					//float scoringCharacterScore = scoringCharacterClusters.getScore(model.getClusterScoreMethod());
					float scoringCharacterScore = scoringCharacterClusters.getScore(model.getLargeClusterScorePercentage());
					
					// ...
					
					//str.append(INDENT + "Scoring special characters score: " + scoringCharacterScore + "\n");
					//retLine = INDENT + "Scoring special characters score: " + scoringCharacterScore;
					retLine = INDENT + "Special characters score: " + myFormatter.format(scoringCharacterScore);
					ret.add(retLine);
					
					score += scoringCharacterScore;
					
					//str.append(scoringCharacterClusters.getWords());   // getWords() does its own indentation and endline. ### ikke helt bra?
					//ret.addAll(scoringCharacterClusters.getDetails());   // getDetails() does its own indentation and endline. ### ikke helt bra?
					indentLevel = 2;   // 2006-04-05
					includeMatchType = false;
					ret.addAll(scoringCharacterClusters.getDetails(indentLevel, includeMatchType));   // getDetails() does its own indentation and endline. ### ikke helt bra?   // 2006-04-05
					
					////////////
					// length //
					////////////
					
					/* 2006-09-20
					 int[] length = new int[Alignment.NUM_FILES];   // length in chars of the relevant elements of each text
					 int[] elementCount = new int[Alignment.NUM_FILES];   // number of relevant elements from each text
					 
					 for (t=0; t<Alignment.NUM_FILES; t++) {
					 length[t] = 0;
					 it = info[t].iterator();
					 while (it.hasNext()) {
					 ElementInfo info1 = (ElementInfo)it.next();
					 length[t] += info1.length;
					 }
					 elementCount[t] = info[t].size();
					 }
					 */
					
					// ...
					float scoreBefore = score;
					//score = SimilarityUtils.adjustForLengthCorrelation(score, length[0], length[1]);
					//score = SimilarityUtils.adjustForLengthCorrelation(score, length[0], length[1], model.getLengthRatio());
					score = SimilarityUtils.adjustForLengthCorrelation(score, length[0], length[1], elementCount[0], elementCount[1], model.getLengthRatio());
					//System.out.println(">>> score = " + score + "\n");
					
					retLine = "Lengths " + length[0] + " (" + myFormatter.format(length[0]*model.getLengthRatio()) + ") and " + length[1];
					if (score > scoreBefore) {
						//str.append("Lengths " + length[0] + " and " + length[1] + " match well,\n" + INDENT + "increasing score from " + scoreBefore + " to " + score + "\n");
						//retLine = "Lengths " + length[0] + " and " + length[1] + " match well,";
						retLine += " match well,";
						ret.add(retLine);
						retLine = INDENT + "increasing score from " + myFormatter.format(scoreBefore) + " to " + myFormatter.format(score);
						ret.add(retLine);
					} else if (score < scoreBefore) {
						//str.append("Lengths " + length[0] + " and " + length[1] + " don't match well,\n" + INDENT + "reducing score from " + scoreBefore + " to " + score + "\n");
						//retLine = "Lengths " + length[0] + " and " + length[1] + " don't match well,";
						retLine += " don't match well,";
						ret.add(retLine);
						retLine = INDENT + "reducing score from " + myFormatter.format(scoreBefore) + " to " + myFormatter.format(score);
						ret.add(retLine);
					} else {
						//str.append("Lengths " + length[0] + " and " + length[1] + " match so-so,\n" + INDENT + "making no change to the score " + score + "\n");
						//retLine = "Lengths " + length[0] + " and " + length[1] + " match so-so,";
						retLine += " match so-so,";
						ret.add(retLine);
						retLine = INDENT + "making no change to the score " + myFormatter.format(score);
						ret.add(retLine);
					}
					
					////////////////////////////////////
					// micro adjustment to break ties // 2005-11-03
					////////////////////////////////////
					
					// when otherwise scoring equal,
					// paths with 1-1's are to preferred
					// over paths with other alignments.
					// add (subtract) micro punishment if step is not 1-1
					boolean is11 = true;
					for (t=0; t<Alignment.NUM_FILES; t++) {
						if (info[t].size() != 1) {
							is11 = false;
						}
					}
					if (!is11) {
						score -= .001;
					}
					
					////////////////////////////////////
					
					//str.insert(0, "Total match score: " + score + "\n");
					retLine = "Total match score: " + myFormatter.format(score);
					// main header. insert at top
					ret.add(0, retLine);
					
				}   // 2006-09-20
				
			}
			//System.out.println("&&& soon leave ElementInfoToBeCompared.toString(). score = " + score);
			
		} else {
			//System.out.print("score already calculated. ");
		}
		
		//System.out.println("leaving toList(). score = " + score);
		
		//// return textual version §§§
		//return new String(str);
		// return textual version as a List
		//System.out.println("exit ElementInfoToBeCompared.toList() ============================");
		return ret;
		
	}
	
}

/**
 *
 */
/**
 * @author boerre
 *
 */
class AlignmentModel {
	
	// when trying to align elements
	// the program will work forward in the text trying out many possible paths
	// before selecting the best one.
	// then it will suggest or select the first step from the best path.
	// the program will try paths of length maxPathLength.
	// Why the 'Max' in the variable name?
	// Well - maxPathLength is max in two senses:
	// - paths will unavoidably be shorter at the very end of the text
	// - paths are built step by step, so intermediate paths are shorter.
	// maxPathLength is settable by the user in the settings dialog.
	// it can be set as low as 1, but not higher than MAX__MAX_PATH_LENGTH.
	
	// 2006-09-20
	
	public static float ELEMENTINFO_SCORE_NOT_CALCULATED = -1.0f;
	public static float ELEMENTINFO_SCORE_HOPELESS = -99999.0f;
	// ###should these two be different values?
	public static float BEST_PATH_SCORE_NOT_CALCULATED = -1.0f;
	public static float BEST_PATH_SCORE_BAD = -1.0f;   // ###perhaps not used in real life
	// end 2006-09-20
	
	Document[] docs;   // package access
	// list of all relevant elements, e.g, <s> elements
	NodeList[] nodes;   // package access. 2004-11-09: flytter denne fra load...thread til hit i model. liste over alle relevante elementer
	// list of all elements, e.g, also <p> elements
	NodeList[] allNodes;   // package access. 2005-09-01. trenger denne fordi: søker etter node med bestemt id. noden kan være på høyere nivå, f.eks <p> i stedet for <s>. og får ikke til å bruke Document.getElementById()
	
	// ########## skulle vært Hashtable?
	
	// alignable elements and their ancestors###
	HashMap<String, Boolean> relevantElementNames = new HashMap<String, Boolean>();
	HashMap<String, Boolean> relevantAncestorElementNames = new HashMap<String, Boolean>();
	
	private DocumentBuilder builder;
	
	protected File currentOpenDirectory;
	protected File currentSaveDirectory;
	
	protected String[] inputFilepath = new String[Alignment.NUM_FILES];
	protected String[] outputFilepath = new String[Alignment.NUM_FILES];
	protected String[] inputFilename = new String[Alignment.NUM_FILES];
	
	protected String anchorFilename = "";
	
	protected String settingsFilename = "";   // 2006-09-21
	
	protected Charset[] charset = new Charset[Alignment.NUM_FILES];   // input files character set. output files character set = input files character set
	
	protected Aligned aligned;
	protected ToAlign toAlign;
	protected Unaligned unaligned;
	
	private String specialCharacters   = Alignment.DEFAULT__SPECIAL_CHARACTERS;
	private String scoringCharacters   = Alignment.DEFAULT__SCORING_CHARACTERS;
	private float lengthRatio          = Alignment.DEFAULT__LENGTH_RATIO;
	private int diceMinWordLength      = Alignment.DEFAULT__DICE_MIN_WORD_LENGTH;
	private float diceMinCountingScore = Alignment.DEFAULT__DICE_MIN_COUNTING_SCORE;
	
	//private int clusterScoreMethod     = Alignment.DEFAULT__CLUSTER_SCORE_METHOD;
	private int largeClusterScorePercentage = Alignment.DEFAULT__LARGE_CLUSTER_SCORE_PERCENTAGE;
	
	private int maxPathLength          = Alignment.DEFAULT__MAX_PATH_LENGTH;
	
	//
	
	private float anchorWordMatchWeight       = Alignment.DEFAULT__ANCHORWORD_MATCH_WEIGHT;
	private float anchorPhraseMatchWeight     = Alignment.DEFAULT__ANCHORPHRASE_MATCH_WEIGHT;
	private float properNameMatchWeight       = Alignment.DEFAULT__PROPERNAME_MATCH_WEIGHT;
	private float diceMatchWeight             = Alignment.DEFAULT__DICE_MATCH_WEIGHT;
	private float dicePhraseMatchWeight       = Alignment.DEFAULT__DICEPHRASE_MATCH_WEIGHT;
	private float numberMatchWeight           = Alignment.DEFAULT__NUMBER_MATCH_WEIGHT;
	private float scoringCharacterMatchWeight = Alignment.DEFAULT__SCORINGCHARACTER_MATCH_WEIGHT;
	
	/*
	 private int outputFileNamingMethod = Alignment.DEFAULT__FILE_NAMING_METHOD;
	 private String fileNamingCorrespExtension = Alignment.DEFAULT__CORRESP_EXTENSION;
	 private String fileNamingNewlineExtension = Alignment.DEFAULT__NEWLINE_EXTENSION;
	 private String fileNamingCorrespSuffix = Alignment.DEFAULT__CORRESP_SUFFIX;
	 private String fileNamingNewlineSuffix = Alignment.DEFAULT__NEWLINE_SUFFIX;
	 */
	
	// filter for newline format ancestor info
	AncestorFilter ancestorFilter = new AncestorFilter(AncestorFilter.MODE_ALLOW, "", "");  // default = allow none = deny all
	
	// 2006-02-23 match info log file
	//protected String logFilename = Alignment.DEFAULT__LOG_FILENAME;
	protected String logFilename = "";
	protected OutputStreamWriter logFileOut;
	boolean logging = false;   // logging on/off (true/false)
	
	protected AnchorWordList anchorWordList;
	protected Compare compare;
	
	//protected AnchorWordMatches anchorWordMatches;   // ### computed at suggest(), but not at unalign()
	//protected MatchInfoDisplayable matchInfoDisplayable;   // ### computed at suggest(), but not at unalign()
	protected MatchInfo matchInfo;   // ### computed at suggest(), but not at unalign()
	
	public AlignmentModel() {   // package access ¤¤¤ nei dette er jo public
		
		////System.out.println("går i gang med å lage model");
		
		// ###hvorfor står disse her? skal de ikke opp blant members?
		setRelevantElementNames(Alignment.DEFAULT__RELEVANT_ELEMENT_NAMES);
		setRelevantAncestorElementNames(Alignment.DEFAULT__RELEVANT_ANCESTOR_ELEMENT_NAMES);
		
		////System.out.println("skal be om å få laget aligned");
		aligned = new Aligned();
		////System.out.println("skal be om å få laget toAlign");
		toAlign = new ToAlign();
		////System.out.println("skal be om å få laget unaligned");
		unaligned = new Unaligned();
		////System.out.println("har fått laget unaligned");
		
		docs = new Document[Alignment.NUM_FILES];
		nodes = new NodeList[Alignment.NUM_FILES];
		allNodes = new NodeList[Alignment.NUM_FILES];
		
		// set up the parser here.
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		factory.setValidating(false);   // #### være et brukervalg???
		//factory.setValidating(true);
		//factory.setNamespaceAware(true);
		
		try {
			builder = factory.newDocumentBuilder();
		} catch (ParserConfigurationException pce) {
			// parser with specified options can't be built
			pce.printStackTrace();
		}
		
		compare = new Compare();
		
		//anchorWordList = new AnchorWordList();
		anchorWordList = new AnchorWordList(AlignmentModel.this);
		
		//anchorWordMatches = new AnchorWordMatches();
		//matchInfoDisplayable = new MatchInfoDisplayable(AlignmentModel.this);
		matchInfo = new MatchInfo(AlignmentModel.this);
		
		/*
		 //The plugin that calculates alignment
		  //2004-02-19: When I have other plugins, there must be a mechanism
		   // to choose different plugins.
		    plugin = new ExistingCorrespPlugin();
		    */
		
	}
	
	public void purge(AlignGui gui) {
		
		// ###dupl kode. se konstruktor.
		// men gir det mening å skille dette ut i en metode,
		// f.eks la konstruktor bruke purge()?
		// #########ikke dupl likevel...
		
		aligned.purge();
		toAlign.purge();
		unaligned.purge();
		
		for (int t=0; t<Alignment.NUM_FILES; t++) {
			docs[t]     = null;
			nodes[t]    = null;
			allNodes[t] = null;
		}
		
		compare.purge();
		
		matchInfo.purge();
		
		gui.statusLine.setText("Cleared");
		
	}
	
	// input = output files character set.
	
	// (first and second files may have different character sets,
	
	// but output character set = input character set)
	
	public Charset getCharset(int t) {
		return charset[t];
	}
	
	public void setCharset(int t, Charset cs) {
		charset[t] = cs;
	}
	
	public void setRelevantElementNames(String string) {
		
		String[] array = string.split(" ");
		relevantElementNames.clear();   // 2006-02-28. denne manglet
		for (int i=0; i<array.length; i++) {
			String name = array[i];
			if (name != "") {
				relevantElementNames.put(name, true);
			}
		}
		
	}
	
	public HashMap getRelevantElementNames() {
		return relevantElementNames;
	}
	
	public String getRelevantElementNamesAsString() {
		String string = "";
		boolean first = true;
		Iterator it = relevantElementNames.keySet().iterator();
		while (it.hasNext()) {
			if (first) {
				first = false;
			} else {
				string += " ";
			}
			string += (String)it.next();
		}
		return string;
	}
	
	public void setRelevantAncestorElementNames(String string) {
		
		String[] array = string.split(" ");
		relevantAncestorElementNames.clear();   // 2006-02-28. denne manglet
		for (int i=0; i<array.length; i++) {
			String name = array[i];
			if (name != "") {
				relevantAncestorElementNames.put(name, true);
			}
		}
		
	}
	
	public HashMap getRelevantAncestorElementNames() {
		return relevantAncestorElementNames;
	}
	
	public String getRelevantAncestorElementNamesAsString() {
		String string = "";
		boolean first = true;
		Iterator it = relevantAncestorElementNames.keySet().iterator();
		while (it.hasNext()) {
			if (first) {
				first = false;
			} else {
				string += " ";
			}
			string += (String)it.next();
		}
		return string;
	}
	
	public boolean getLogging() {
		//System.out.println("getLogging() kalt. logging = " + logging);
		return logging;
	}
	
	public void setLogging(boolean logging) {
		this.logging = logging;
	}
	
	public String getSpecialCharacters() {
		return specialCharacters;
	}
	
	public void setSpecialCharacters(String specialCharacters) {
		this.specialCharacters = specialCharacters;
	}
	
	// ### to metoder med samme navn ###
	public String getScoringCharacters() {
		return scoringCharacters;
	}
	
	public void setScoringCharacters(String scoringCharacters) {
		this.scoringCharacters = scoringCharacters;
	}
	
	public float getLengthRatio() {
		return lengthRatio;
	}
	
	public void setLengthRatio(float lengthRatio) {
		this.lengthRatio = lengthRatio;
	}
	
	public int getDiceMinWordLength() {
		return diceMinWordLength;
	}
	
	public void setDiceMinWordLength(int diceMinWordLength) {
		this.diceMinWordLength = diceMinWordLength;
	}
	
	public float getDiceMinCountingScore() {
		return diceMinCountingScore;
	}
	
	public void setDiceMinCountingScore(float diceMinCountingScore) {
		this.diceMinCountingScore = diceMinCountingScore;
	}
	
	/*
	 public int getClusterScoreMethod() {
	 return clusterScoreMethod;
	 }
	 
	 public void setClusterScoreMethod(int clusterScoreMethod) {
	 this.clusterScoreMethod = clusterScoreMethod;
	 }
	 */
	
	public int getLargeClusterScorePercentage() {
		return largeClusterScorePercentage;
	}
	
	public void setLargeClusterScorePercentage(int largeClusterScorePercentage) {
		this.largeClusterScorePercentage = largeClusterScorePercentage;
	}
	
	//
	
	public float getAnchorWordMatchWeight() {
		return anchorWordMatchWeight;
	}
	
	public float getAnchorPhraseMatchWeight() {
		return anchorPhraseMatchWeight;
	}
	
	public float getProperNameMatchWeight() {
		return properNameMatchWeight;
	}
	
	public float getDiceMatchWeight() {
		return diceMatchWeight;
	}
	
	public float getDicePhraseMatchWeight() {
		return dicePhraseMatchWeight;
	}
	
	public float getNumberMatchWeight() {
		return numberMatchWeight;
	}
	
	public float getScoringCharacterMatchWeight() {
		return scoringCharacterMatchWeight;
	}
	
	public void setMatchWeights(
			float anchorWordMatchWeight,
			float anchorPhraseMatchWeight,
			float properNameMatchWeight,
			float diceMatchWeight,
			float dicePhraseMatchWeight,
			float numberMatchWeight,
			float scoringCharacterMatchWeight
	) {
		this.anchorWordMatchWeight       = anchorWordMatchWeight;
		this.anchorPhraseMatchWeight     = anchorPhraseMatchWeight;
		this.properNameMatchWeight       = properNameMatchWeight;
		this.diceMatchWeight             = diceMatchWeight;
		this.dicePhraseMatchWeight       = dicePhraseMatchWeight;
		this.numberMatchWeight           = numberMatchWeight;
		this.scoringCharacterMatchWeight = scoringCharacterMatchWeight;
		
	}
	
	// 2006-09-21. ###duplisert, men lager disse for å gjøre det lett for meg selv når jeg nå skal implementere innlesing fra settingsfil ved oppstart
	
	public void setAnchorWordMatchWeight(float anchorWordMatchWeight) {
		this.anchorWordMatchWeight       = anchorWordMatchWeight;
	}
	
	public void setAnchorPhraseMatchWeight(float anchorPhraseMatchWeight) {
		this.anchorPhraseMatchWeight     = anchorPhraseMatchWeight;
	}
	
	public void setProperNameMatchWeight(float properNameMatchWeight) {
		this.properNameMatchWeight       = properNameMatchWeight;
	}
	
	public void setDiceMatchWeight(float diceMatchWeight) {
		this.diceMatchWeight             = diceMatchWeight;
	}
	
	public void setDicePhraseMatchWeight(float dicePhraseMatchWeight) {
		this.dicePhraseMatchWeight       = dicePhraseMatchWeight;
	}
	
	public void setNumberMatchWeight(float numberMatchWeight) {
		this.numberMatchWeight           = numberMatchWeight;
	}
	
	public void setScoringCharacterMatchWeight(float scoringCharacterMatchWeight) {
		this.scoringCharacterMatchWeight = scoringCharacterMatchWeight;
	}
	// end 2006-09-21
	
	//
	
	public int getMaxPathLength() {
		return maxPathLength;
	}
	
	public void setMaxPathLength(int maxPathLength) {
		this.maxPathLength = maxPathLength;
	}
	
	/*
	 public int getOutputFileNamingMethod() {
	 return outputFileNamingMethod;
	 }
	 
	 public void setOutputFileNamingMethod(int outputFileNamingMethod) {
	 this.outputFileNamingMethod = outputFileNamingMethod;
	 }
	 
	 public String getFileNamingCorrespExtension() {
	 return fileNamingCorrespExtension;
	 }
	 
	 public void setFileNamingCorrespExtension(String fileNamingCorrespExtension) {
	 this.fileNamingCorrespExtension = fileNamingCorrespExtension;
	 }
	 
	 public String getFileNamingNewlineExtension() {
	 return fileNamingNewlineExtension;
	 }
	 
	 public void setFileNamingNewlineExtension(String fileNamingNewlineExtension) {
	 this.fileNamingNewlineExtension = fileNamingNewlineExtension;
	 }
	 
	 public String getFileNamingCorrespSuffix() {
	 return fileNamingCorrespSuffix;
	 }
	 
	 public void setFileNamingCorrespSuffix(String fileNamingCorrespSuffix) {
	 this.fileNamingCorrespSuffix = fileNamingCorrespSuffix;
	 }
	 
	 public String getFileNamingNewlineSuffix() {
	 return fileNamingNewlineSuffix;
	 }
	 
	 public void setFileNamingNewlineSuffix(String fileNamingNewlineSuffix) {
	 this.fileNamingNewlineSuffix = fileNamingNewlineSuffix;
	 }
	 */
	
	public AncestorFilter getAncestorFilter() {
		//System.out.println("getAncestorFilter()");
		return ancestorFilter;
	}
	
	//public void setAncestorInfoMode(int mode) {
	//	ancestorInfoMode = mode;
	//}
	
	public void setAncestorInfo(int mode, String elementNames, String attributeNames) {
		//System.out.println("setAncestorInfo(). mode = " + mode);
		setAncestorInfoElementNames(elementNames);
		setAncestorInfoAttributeNames(attributeNames);
		// mode parameter is radio button choice 0-3
		if (mode == AncestorInfoRadioButtonPanel.NONE) {
			ancestorFilter.setMode(AncestorFilter.MODE_ALLOW);
			clearAncestorInfoElementNames();
			clearAncestorInfoAttributeNames();
		} else if (mode == AncestorInfoRadioButtonPanel.ALL) {
			ancestorFilter.setMode(AncestorFilter.MODE_DENY);
			clearAncestorInfoElementNames();
			clearAncestorInfoAttributeNames();
		} else if (mode == AncestorInfoRadioButtonPanel.ALLOW) {
			ancestorFilter.setMode(AncestorFilter.MODE_ALLOW);
		} else if (mode == AncestorInfoRadioButtonPanel.DENY) {
			ancestorFilter.setMode(AncestorFilter.MODE_DENY);
		} else {
			// ### program error ###
			ancestorFilter.setMode(AncestorFilter.MODE_ALLOW);   // ###dodgy??
		}
	}
	
	// 2006-09-21
	// ###for enkelhets skyld
	public void setAncestorInfoChoice(int mode) {
		// mode parameter is radio button choice 0-3
		if (mode == AncestorInfoRadioButtonPanel.NONE) {
			ancestorFilter.setMode(AncestorFilter.MODE_ALLOW);
			////clearAncestorInfoElementNames();
			////clearAncestorInfoAttributeNames();
		} else if (mode == AncestorInfoRadioButtonPanel.ALL) {
			ancestorFilter.setMode(AncestorFilter.MODE_DENY);
			////clearAncestorInfoElementNames();
			////clearAncestorInfoAttributeNames();
		} else if (mode == AncestorInfoRadioButtonPanel.ALLOW) {
			ancestorFilter.setMode(AncestorFilter.MODE_ALLOW);
		} else if (mode == AncestorInfoRadioButtonPanel.DENY) {
			ancestorFilter.setMode(AncestorFilter.MODE_DENY);
		} else {
			// ### program error ###
			ancestorFilter.setMode(AncestorFilter.MODE_ALLOW);   // ###dodgy??
		}
	}
	// end 2006-09-21
	
	public int getAncestorInfoChoice() {   // 0 =
		//System.out.println("getAncestorInfoChoice(). ancestorFilter.mode = " + ancestorFilter.mode + ", ancestorFilter.noElements() = " + ancestorFilter.noElements());
		if (ancestorFilter.mode == AncestorFilter.MODE_ALLOW) {
			if (ancestorFilter.noElements()) {
				return AncestorInfoRadioButtonPanel.NONE;   // ###ugly?
			} else {
				return AncestorInfoRadioButtonPanel.ALLOW;   // ###ugly?
			}
		} else {
			if (ancestorFilter.noElements()) {
				return AncestorInfoRadioButtonPanel.ALL;   // ###ugly?
			} else {
				return AncestorInfoRadioButtonPanel.DENY;   // ###ugly?
			}
		}
	}
	
	public String getAncestorInfoElementNamesAsString() {
		//System.out.println("getAncestorInfoElementNamesAsString()");
		return ancestorFilter.getElementNamesAsString();
	}
	
	public String getAncestorInfoAttributeNamesAsString() {
		//System.out.println("getAncestorInfoAttributeNamesAsString()");
		return ancestorFilter.getAttributeNamesAsString();
	}
	
	//private void setAncestorInfoElementNames(String names) {
	public void setAncestorInfoElementNames(String names) {
		//System.out.println("setAncestorInfoElementNames(String names). names = " + names);
		ancestorFilter.setElementNames(names);
	}
	
	private void clearAncestorInfoElementNames() {
		
		ancestorFilter.setElementNames("");
	}
	
	//private void setAncestorInfoAttributeNames(String names) {
	public void setAncestorInfoAttributeNames(String names) {
		//System.out.println("setAncestorInfoAttributeNames(String names). names = " + names);
		ancestorFilter.setAttributeNames(names);
	}
	
	private void clearAncestorInfoAttributeNames() {
		ancestorFilter.setAttributeNames("");
	}
	
	//public void setAncestorInfoElementNames(String names) {
	//	String[] array = names.split(" ");
	//	ancestorInfoElementNames.clear();
	//	for (int i=0; i<array.length; i++) {
	//		String name = array[i];
	//		if (name != "") {
	//			ancestorInfoElementNames.put(name, true);
	//		}
	//	}
	//}
	
	//public HashMap getAncestorInfoElementNames() {
	//	return ancestorInfoElementNames;
	//}
	
	//public void setAncestorInfoAttributeNames(String names) {
	//	String[] array = names.split(" ");
	//	ancestorInfoAttributeNames.clear();
	//	for (int i=0; i<array.length; i++) {
	//		String name = array[i];
	//		if (name != "") {
	//			ancestorInfoAttributeNames.put(name, true);
	//		}
	//	}
	//}
	
	//public HashMap getAncestorInfoAttributeNames() {
	//	return ancestorInfoAttributeNames;
	//}
	
	// 2006-02-23 match info log file
	public String getLogFilename() {
		return logFilename;
	}
	
	public void setLogFilename(String logFilename) {
		this.logFilename = logFilename;
	}
	
	public void setAnchorFilename(String anchorFilename) {
		this.anchorFilename = anchorFilename;
	}
	
	// 2006-09-21
	public void setSettingsFilename(String settingsFilename) {
		this.settingsFilename = settingsFilename;
	}
	// end 2006-09-21
	
	/*
	 // 2006-09-22
	  public void loadSettingsFile() {
	  // try to load the settings file with the name in variable settingsFilename.
	   // the file might not exist
	    Toolkit.getDefaultToolkit().beep();
	    System.out.println("loadSettingsFile() ikke implementert. settingsFilename=" + settingsFilename);
	    }
	    // end 2006-09-22
	     */
	
	//
	
	// 2006-10-03
	void setMemoryUsage(AlignGui gui) {
		//System.out.println("model sin setMemoryUsage()");
		gui.statusLine.setMemoryUsage();
	}
	// end 2006-10-03
	
	void updateAlignedTotalRatio(AlignGui gui) {
		
		//System.out.println("updateAlignedTotalRatio()");
		String text = "Aligned: ";
		for (int t=0; t<Alignment.NUM_FILES; t++) {
			if (t > 0) { text += " - "; }
			text += Integer.toString(getLowestUnalignedElementNumber(t) + 1) + "/" + nodes[t].getLength();
		}
		gui.statusLine.setText(text);
		// 2006-10-03. ###disse funker ikke. må jeg yielde på en eller annen måte????
		//gui.statusLine.invalidate();   // 2006-08-14
		//gui.statusLine.validate();   // 2006-08-14
		System.out.println(text);   // 2006-10-03
		
	}
	
	int getLowestUnalignedElementNumber(int t) {
		
		// lowest unaligned or under consideration
		
		if (toAlign.elements[t].size() > 0) {
			return ((AElement)(toAlign.elements[t].get(0))).elementNumber;
		} else if (unaligned.elements[t].size() > 0) {
			return ((AElement)(unaligned.elements[t].get(0))).elementNumber;
		} else {
			return nodes[t].getLength() - 1;
		}
		// ### AlignGui gui,
		// ### gui.model.
		
	}
	
	//
	
	/**
	 * ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤Loads an xml file.
	 * @return true if loading was successful, false if there was an error.
	 *         (most likely a parsing error)
	 */
	//void loadFile(AlignGui gui, File f, int t) {   // package access
	//void loadFile(AlignGui gui, File f, int t) throws EmptyElementException {   // package access // 2006-09-19
	void loadFile(AlignGui gui, File f, int t) throws Exception {   // package access   // 2006-09-22
		
		//void loadFile(File f, int t) {   // package access
		
		////System.out.println("f = " + f);
		// ...
		Document result = null;
		
		try {
			
			// make DOM tree from xml file
			////gui.counterDoc.insertString(0, "file -> DOM", null);
			//gui.counter.setText("file -> DOM");
			gui.statusLine.setText("File -> DOM");
			result = builder.parse(f);
			//System.out.println("File " + f.getName() + " loaded as text " + t+1);
			//System.out.println("File " + f.getName() + " loaded as text " + (t+1));
			
			//¤¤¤2006-02-28. for å kunne lagre utfil med samme encoding som innfil
			Charset cs = Charset.forName(result.getXmlEncoding());
			setCharset(t, cs);
			
			//// ### gjør dette kun for å fortelle hvor mange elementer det er?
			//// ### men det er jo ikke direkte child nodes vi er interessert i. disse kan jo være <p> f.eks
			//NodeList childNodes = result.getChildNodes();
			//System.out.println("Child node count: " + childNodes.getLength());
			//childNodes = null;
			
			docs[t] = result;
			
			// 2006-09-19
		} catch (Exception e) {   // ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
			
			ErrorMessage.error("Exception (1) when loading text " + (t+1) + " " + f.getName() + ":\n" + e.toString());   // 2006-08-10
			
		}
		// end 2006-09-19
		
		// get a list of alignable elements from the DOM tree
		try {   // 2006-09-19
			nodes[t] = getElements(t);
			//} catch (EmptyElementException e) {   // 2006-09-19
		} catch (Exception e) {   // 2006-09-22
			throw e;   // ###   // 2006-09-19
		}   // 2006-09-19
		
		try {   // 2006-09-19
			
			// get a list of all elements
			allNodes[t] = docs[t].getElementsByTagName("*");
			
			// ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤dette er ikke skikkelig
			
			// clear ...
			//System.out.println("*** do clear stuff here? ***");
			
			// fill the unaligned list boxes with a suitable version of the elements
			//System.out.println("Element count: " + nodes[t].getLength());
			
			// process element list.
			// update GUI while processing element list.
			// do processing in separate thread so GUI elements can be updated.
			Thread load = new LoadFileThread(gui, nodes, t);
			load.start();
			
			//// init aligned/total ratio in status line
			// ### funker visst ikke, men skitt i det
			//updateAlignedTotalRatio(gui);
			// ### aha. metoden vil jo ikke funke når bare én fil er lest inn.
			// ### og når fil nr to er lest inn, vil vi vel at det skal stå "Parsed",
			// og ikke overskrive dette med "0/9999 - 0/9999" - ?
			
			// remember name and full pathname of input file
			gui.model.inputFilepath[t] = f.getCanonicalPath();
			gui.model.inputFilename[t] = f.getName();
			
		} catch (Exception e) {   // ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
			
			//System.err.println("Exception when loading " + t + " " + f.getName() + ": ");
			//System.err.println(e.toString());
			//ErrorMessage.error("Exception when loading " + t + " " + f.getName() + ":\n" + e.toString());   // 2006-08-10
			ErrorMessage.error("Exception (2) when loading text " + (t+1) + " " + f.getName() + ":\n" + e.toString());   // 2006-08-10
			//e.printStackTrace();
			
			//return false;
			
		}
		
	}

	/*
	 * Added by boerre
	 * Loads an xml file, without gui
	 */
	void loadTobeAlignedFile(File f, int t) throws Exception {
		Document result = null;
	
		try {
			result = builder.parse(f);
			Charset cs = Charset.forName(result.getXmlEncoding());
			setCharset(t, cs);
			docs[t] = result;
		} catch (Exception e) {   // ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
			ErrorMessage.error("Exception (1) when loading text " + (t+1) + " " + f.getName() + ":\n" + e.toString());
		}
		
		try {
			nodes[t] = getElements(t);
		} catch (Exception e) {
			throw e;
		}
	
		try {
			allNodes[t] = docs[t].getElementsByTagName("*");
		} catch (Exception e) {   // ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
			ErrorMessage.error("Exception (2) when loading text " + (t+1) + " " + f.getName() + ":\n" + e.toString());
		}
	
	}
	

	//private NodeList getElements(int t) {
	//private NodeList getElements(int t) throws EmptyElementException {   // 2006-09-19
	private NodeList getElements(int t) throws Exception {   // 2006-09-22
		
		//return docs[t].getElementsByTagName("s");
		//return docs[t].getElementsByTagName("p"); funker
		// ### klønete
		String[] relevantElementNamesArray = new String[relevantElementNames.size()];
		Iterator it = relevantElementNames.keySet().iterator();
		int count = 0;
		while (it.hasNext()) {
			String name = (String)it.next();
			relevantElementNamesArray[count] = name;
			count++;
		}
		try {   // 2006-09-19
			//return XmlTools.getElementsByTagNames(docs[t], relevantElementNamesArray);
			return XmlTools.getElementsByTagNames(docs[t], relevantElementNamesArray, getSpecialCharacters());   // 2006-10-03
			//} catch (EmptyElementException e) {   // 2006-09-19
		} catch (Exception e) {   // 2006-09-22
			throw e;   // ###   // 2006-09-19
		}   // 2006-09-19
		
	}
	
	/**
	 * establishes corresp attributes in dom for text t
	 */
	void setCorrespAttributes(int t) {
		
		Iterator it;
		Iterator eIt;
		
		// clean dom of corresp attributes that may have been in the input file
		
		for (int i=0; i<((NodeList)(nodes[t])).getLength(); i++) {
			Element el = (Element)(((NodeList)(nodes[t])).item(i));
			el.removeAttribute("corresp");
		}
		
		// set new corresp attributes in dom.
		// loop through all finished alignments
		
		String newAttribute;
		it = aligned.alignments.iterator();
		while (it.hasNext()) {
			
			// next alignment
			Link link = (Link)(it.next());
			// get the corresp attribute values from all the other texts.
			// loop through all the other texts
			newAttribute = "";
			for (int tt=0; tt<Alignment.NUM_FILES; tt++) {
				if (tt != t) {
					
					// other text.
					
					// inspect the elements the alignment has got in this other text
					// and find id's for the corresp attribute to refer to
					
					if (link.elementNumbers[tt].size() == 0) {
						
						//System.out.println("t = " + t + ". tt = " + tt + ". the alignment has no elements in this other text");
						// the alignment has no elements in this other text.
						// try to find something else for the corresp attribute to refer to -
						// a node one level up.
						// e.g, let an <s> refer to a <p>.
						// challenge: find the correct <p> (or similar)
						
						// check with previous siblings -
						// e.g, previous <s>'s in the same <p> -
						// what they refer to in the other text
						
						// which element to start with?
						//System.out.println("which element to start with?");
						// there might be more than one element from this text in the alignment.
						// find the first one
						
						int smallestElementNumber = Integer.MAX_VALUE;
						eIt = link.elementNumbers[t].iterator();
						while (eIt.hasNext()) {
							int elementNumber = ((Integer)(eIt.next())).intValue();
							if (elementNumber < smallestElementNumber) {
								smallestElementNumber = elementNumber;
							}
						}
						
						Node el = nodes[t].item(smallestElementNumber);
						//System.out.println("el. name = " + el.getNodeName() + ". type = " + el.getNodeType() + ". id = " + ((Element)el).getAttribute("id"));
						
						//System.out.println("look for previous");
						Node prevEl = XmlTools.getPreviousRelevantSiblingElement(el, relevantElementNames);
						String otherId = "";
						while (prevEl != null) {
							//System.out.println("prevEl. name = " + prevEl.getNodeName() + ". type = " + prevEl.getNodeType());
							if (((Element)prevEl).getAttribute("corresp") != "") {
								
								// found a sibling which refers to #####this other text.
								// get its last corresp attribute value (if more than one)
								
								String[] values = ((Element)prevEl).getAttribute("corresp").split(" ");
								otherId = values[values.length-1];
								//System.out.println("t = " + t + ". tt = " + tt + ". otherId = " + otherId);
								break;
								
							} else {
								
								prevEl = XmlTools.getPreviousRelevantSiblingElement(prevEl, relevantElementNames);
								
							}
						}
						//System.out.println("otherId = " + otherId);
						
						if (otherId == "") {
							
							// no previous sibling refers to the other text.
							//System.out.println("no previous sibling refers to the other text");
							// must try to consult elements in the previous parent element
							//System.out.println("must try to consult elements in the previous parent element");
							
							// first up one level
							//System.out.println("first up one level");
							
							Node parent = XmlTools.getRelevantAncestorElement(el, relevantAncestorElementNames);
							if (parent == null) {
								
								// no higher level.
								//System.out.println("no higher level");
								// no reason to believe there's a higher level in the other text either.
								// refer to nothing
								
								newAttribute = "";
								
							} else {
								
								// then to previous sibling (previous parent)
								//System.out.println("then to previous sibling (previous parent)");
								
								Node prevParent = XmlTools.getPreviousRelevantSiblingElement(parent, relevantAncestorElementNames);
								if (prevParent == null) {
									
									// no sibling. first parent.
									//System.out.println("no sibling. first parent");
									// then it's the first parent in the other text we want.
									// first get the first element in the other text
									
									Node otherElement = nodes[tt].item(0);
									
									// then get its parent
									
									Node otherParent = XmlTools.getRelevantAncestorElement(otherElement, relevantAncestorElementNames);
									if (otherParent == null) {
										
										// no parent.
										// refer to nothing
										
										newAttribute = "";
										
									} else {
										
										// refer to that parent
										
										newAttribute = ((Element)otherParent).getAttribute("id");
										//System.out.println("refer to that otherParent. t = " + t + ". tt = " + tt + ". newAttribute = " + newAttribute);
										
									}
									
								} else {
									
									// found previous parent.
									//System.out.println("found previous parent");
									// try its children (###which hopefully are on the right level,
									// and not e.g on a level between <p> and <s>).
									// work backwards from last child
									//System.out.println("try its children. work backwards from last child");
									
									prevEl = XmlTools.getRelevantLastDescendantElement(prevParent, relevantElementNames);
									if (prevEl == null) {
										
										// no children.
										//System.out.println("no children");
										// could be e.g empty <p>,
										// or e.g some irrelevant element between <p>'s.
										// give up.
										// refer to nothing
										
										newAttribute = "";
										
									} else {
										
										// ...
										//System.out.println("there are children");
										
										otherId = "";
										while (prevEl != null) {
											//System.out.println("look for child with corresp");
											if (((Element)prevEl).getAttribute("corresp") != "") {
												
												// found a sibling which refers to #####this other text.
												//System.out.println("found a sibling which refers to #####this other text");
												// get its last corresp attribute value (if more than one)
												//System.out.println("get its last corresp attribute value (if more than one)");
												
												String[] values = ((Element)prevEl).getAttribute("corresp").split(" ");
												//System.out.println("values.length() = " + values.length() + ", values[0] = " + values[0]);   // ###
												otherId = values[values.length-1];
												//System.out.println("t = " + t + ". tt = " + tt + ". otherId = " + otherId);
												break;
												
											} else {
												
												prevEl = XmlTools.getPreviousRelevantSiblingElement(prevEl, relevantElementNames);
												
											}
										}
										
										if (otherId == "") {
											
											// no children of previous parent refer to the other text.
											// ...
											// give up.
											// refer to nothing
											
											newAttribute = "";
											
										} else {
											
											// found reference to the other text
											//System.out.println("found reference to the other text");
											// get element in the other text
											// reference to which level?
											//System.out.println("which level?");
											
											//Node otherEl = docs[tt].getElementById(otherId);   // ### funker ikke????!!!!
											Node otherEl = XmlTools.getElementByIdInNodeList(allNodes[tt], otherId);  // ### gjør dette isteden
											
											if (relevantElementNames.containsKey(otherEl.getNodeName())) {
												
												// the "relevant" level
												//System.out.println("the 'relevant' level");
												
												// get its parent.
												//System.out.println("get its parent");
												// up one level in the other text
												
												Node otherParent = XmlTools.getRelevantAncestorElement(otherEl, relevantAncestorElementNames);
												//System.out.println("parent has id = " + ((Element)otherParent).getAttribute("id"));
												if (otherParent == null) {
													
													// no higher level.
													//System.out.println("no higher level");
													// give up.
													// refer to nothing
													
													newAttribute = "";
													
												} else {
													
													// then to next sibling (next parent)
													//System.out.println("then to next sibling (next parent)");
													
													Node nextOtherParent = XmlTools.getNextRelevantSiblingElement(otherParent, relevantAncestorElementNames);
													if (nextOtherParent == null) {
														
														// no next sibling (next parent).
														//System.out.println("no next sibling (next parent)");
														// give up
														// refer to nothing
														
														newAttribute = "";
														
													} else {
														
														// refer to that next sibling (next parent),
														// which hopefully is "in synch"
														// with the current element and its parent
														
														newAttribute = ((Element)nextOtherParent).getAttribute("id");
														//System.out.println("refer to that next sibling (next parent). t = " + t + ". tt = " + tt + ". newAttribute = " + newAttribute);
														
													}
													
												}
												
											} else {
												
												// something else, i.e, parent level.
												//System.out.println("something else, i.e, parent level");
												// refer to that parent
												
												newAttribute = otherId;
												
											}
											
										}
										
									}
									
								}
								
							}
							
							
						} else {
							
							// found a previous sibling with a reference to the other text.
							// reference to which level?
							
							//System.out.println("found a previous sibling with a reference to the other text. otherId = " + otherId);
							//System.out.println("t = " + t);
							//System.out.println("tt = " + tt);
							//System.out.println("docs[tt].getXmlVersion() = " + docs[tt].getXmlVersion());
							//Node otherEl = docs[tt].getElementById(otherId);   // ### funker ikke????!!!!
							Node otherEl = XmlTools.getElementByIdInNodeList(allNodes[tt], otherId);  // ### gjør dette isteden
							//System.out.println("otherEl = " + otherEl);
							//System.out.println("otherEl.getNodeName() = " + otherEl.getNodeName());
							
							if (relevantElementNames.containsKey(otherEl.getNodeName())) {
								
								// the "relevant"level
								
								// get its parent
								
								Node otherParent = XmlTools.getRelevantAncestorElement(otherEl, relevantAncestorElementNames);
								
								if (otherParent == null) {
									
									// has no parent.
									// refer to nothing
									
									newAttribute = "";
									
								} else {
									
									// refer to that parent
									
									newAttribute = ((Element)otherParent).getAttribute("id");
									
								}
								
							} else {
								
								// something else, i.e, parent level.
								// refer to that parent
								
								newAttribute = otherId;
								
							}
							
						}
						
						
					} else {
						
						// loop through the elements the alignment has got in this other text
						
						eIt = link.elementNumbers[tt].iterator();
						while (eIt.hasNext()) {
							int elementNumber = ((Integer)(eIt.next())).intValue();
							String id = ((Element)(((AElement)(aligned.elements[tt].get(elementNumber))).element)).getAttribute("id");
							if (newAttribute != "") {
								newAttribute += " ";
							}
							newAttribute += id;
						}
						
					}
					
				}
			}
			
			// set the corresp attribute values in all the elements
			// the alignment has got in the current text
			eIt = link.elementNumbers[t].iterator();
			while (eIt.hasNext()) {
				int elementNumber = ((Integer)(eIt.next())).intValue();
				((Element)(((AElement)(aligned.elements[t].get(elementNumber))).element)).setAttribute("corresp", newAttribute);
			}
			
		}
		
	}
	
	/**
	 * Saves an xml file with corresp attributes
	 */
	//void saveFile(AlignGui gui, File f, int t) {   // package access
	//void saveFile(File f, int t) {   // package access
	//void saveCorrespFormatFile(File f, int t) {   // package access
	void saveCorrespFormatFile(File f, int t, Charset cs) {   // package access
		
		// ¤¤¤ burde vært advarsel hvis pending alignments?
		// hvis unaligned?
		// komme spørsmål om prog skal sette et merke?
		
		//// establish corresp attributes in dom for text t
		// ### nei, gjør det på forhånd
		//setCorrespAttributes(t);
		
		// write dom to file
		
		//XmlOutput.writeXml(docs[t], f);
		XmlOutput.writeXml(docs[t], f, cs);
		
	}
	
	/**
	 * Saves file in newline format
	 */
	//void saveNewlineFormatFile(File f, int t) {   // package access
	//void saveNewlineFormatFile(File f, int t, Charset cs) {   // package access
	void saveNewlineFormatFile(File f, int t, Charset cs, AncestorFilter filter) {   // package access
		
		//System.out.println("filter = " + filter);
		
		// ¤¤¤ burde vært advarsel hvis pending alignments?
		// hvis unaligned?
		// komme spørsmål om prog skal sette et merke?
		
		Iterator it;
		Iterator eIt;
		
		// clean dom of corresp attributes
		
		for (int i=0; i<((NodeList)(nodes[t])).getLength(); i++) {
			Element el = (Element)(((NodeList)(nodes[t])).item(i));
			el.removeAttribute("corresp");
		}
		
		// ...
		
		//FileWriter out;
		OutputStreamWriter out;
		try {
			
			//out = new FileWriter(f);
			//¤¤¤endringer 2006-02-20 for å kunne skrive utf-8, o.a
			OutputStream fOut= new FileOutputStream(f);
			OutputStream bOut= new BufferedOutputStream(fOut);
			out = new OutputStreamWriter(bOut, cs);
			
		} catch (IOException e1) {
			
			// ### ### ### ### ### ### ### ### ### ### ### ### ###
			Toolkit.getDefaultToolkit().beep();
			System.out.println("Program error? Can't create new FileWriter");
			return;
			
		}
		
		// loop through all finished alignments and write to file
		
		it = aligned.alignments.iterator();
		while (it.hasNext()) {
			// next alignment
			Link link = (Link)(it.next());
			// loop through the alignment's elements
			String line = "";
			boolean first = true;
			eIt = link.elementNumbers[t].iterator();
			while (eIt.hasNext()) {
				int elementNumber = ((Integer)(eIt.next())).intValue();
				//Element element = (Element)(((AElement)(aligned.elements[t].get(elementNumber))).element);
				//String elementText = XmlTools.getText(element);   ### heller bruke .getTextContent()
				AElement aElement = (AElement)(aligned.elements[t].get(elementNumber));
				//String elementText = aElement.toString();
				// ###toNewString(): some users might like parent info prepended to the elements
				// in their newline format output files
				String elementText = aElement.toNewString(filter);
				if (first) {
					first = false;
				} else {
					line += " ";
				}
				line += elementText;
			}
			try {
				out.write(line + "\n");
			} catch (IOException e2) {
				// ### ### ### ### ### ### ### ### ### ### ### ### ###
				Toolkit.getDefaultToolkit().beep();
				System.out.println("Program error? Can't do out.write");
				try {
					out.close();
				} catch (IOException e3) {
					// ### ### ### ### ### ### ### ### ### ### ### ### ###
					Toolkit.getDefaultToolkit().beep();
					System.out.println("Program error? Can't do out.close");
					return;
				}
				return;
			}
			
		}
		
		try {
			out.close();
		} catch (IOException e4) {
			// ### ### ### ### ### ### ### ### ### ### ### ### ###
			Toolkit.getDefaultToolkit().beep();
			System.out.println("Program error? Can't do out.close");
			return;
		}
		
		// what if there are unfinished ones?
		
		//...
		
	}
	
	/**
	 * Saves file in "external" format
	 */
	void saveExternalFormatFile(File f) {
		
		// ¤¤¤ samme spm som for de andre formatene
		
		// establish corresp attributes in dom for all texts.
		// ### no - not necessary if already saved in "corresp" format
		
		// This is necessary so that all data is written to the file. 
                // Otherwise the file gets no references
		for (int t=0; t<Alignment.NUM_FILES; t++) {
			setCorrespAttributes(t);
		}
		
		OutputStreamWriter out;
		
		try {
			
			// output is always utf-8
			OutputStream fOut = new FileOutputStream(f);
			OutputStream bOut = new BufferedOutputStream(fOut);
			Charset cs = Charset.forName("UTF-8");
			out = new OutputStreamWriter(bOut, cs);
			
		} catch (IOException e) {
			
			// ¤¤¤ PLAIN_MESSAGE, INFORMATION_MESSAGE, WARNING_MESSAGE, ERROR_MESSAGE?
			JOptionPane.showMessageDialog(
					null,
					"Can't save file " + f.getName(),
					//"¤¤¤Title",
					"Error",   // 2006-09-21
					JOptionPane.ERROR_MESSAGE
			);
			//System.err.println("Exception when saving " + f.getName() + ": ");
			//System.err.println(e.toString());
			ErrorMessage.error("Exception when saving " + f.getName() + ":\n" + e.toString());   // 2006-08-10
			//e.printStackTrace();
			
			return;
			
		}
		
		//////////bollocks int p = 0;   // current position in output file
		
		// create and output header
		
		String data = "<?xml version='1.0' encoding='utf-8'?>\n";
		// #######mye dupl kode try/except/feilhåndtering
		try {
			out.write(data, 0, data.length());
		} catch (IOException e) {
			JOptionPane.showMessageDialog(
					null,
					"Can't write to file " + f.getName(),
					//"¤¤¤Title",
					"Error",   // 2006-09-21
					JOptionPane.ERROR_MESSAGE
			);
			//System.err.println("Exception when writing to " + f.getName() + ": ");
			//System.err.println(e.toString());
			ErrorMessage.error("Exception when writing to " + f.getName() + ":\n" + e.toString());   // 2006-08-10
			return;
		}
		
		// create and output root start element
		data = "<linkGrp targType='" + "..." + "' toDoc='" + inputFilename[0] + "' fromDoc='" + inputFilename[1] + "'>\n";
		try {
			out.write(data, 0, data.length());
		} catch (IOException e) {
			JOptionPane.showMessageDialog(
					null,
					"Can't write to file " + f.getName(),
					//"¤¤¤Title",
					"Error",   // 2006-09-21
					JOptionPane.ERROR_MESSAGE
			);
			//System.err.println("Exception when writing to " + f.getName() + ": ");
			//System.err.println(e.toString());
			ErrorMessage.error("Exception when writing to " + f.getName() + ":\n" + e.toString());   // 2006-08-10
			return;
		}
		
		// loop through all finished alignments and write to file
		
		Iterator it = aligned.alignments.iterator();
		while (it.hasNext()) {
			
			// next alignment.
			// get all the id's to link
			Link link = (Link)(it.next());
			String xtargetsValue = "";
			// loop through the texts
			for (int t=0; t<Alignment.NUM_FILES; t++) {
				// to get the relevant id's from text t
				// look at the corresp attribute in the alignment's first element in the _other_ text.
				// why do it via the _other_ text?
				// because we may pick up links to elements on a higher level
				int tt = 1 - t;   // ##############
				String ids = "";
				if (link.elementNumbers[tt].size() > 0) {
					int firstElementNumber = ((Integer)(((TreeSet)(link.elementNumbers[tt])).first())).intValue();
					// get the corresp attribute
					//System.out.println((AElement)(aligned.elements[tt].get(firstElementNumber)));
					ids = ((Element)(((AElement)(aligned.elements[tt].get(firstElementNumber))).element)).getAttribute("corresp");
				} else {
					// the alignment has no element in the other text.
					// get the id's from the alignment's elements in _this_ text
					Iterator eIt = link.elementNumbers[t].iterator();
					while (eIt.hasNext()) {
						int elementNumber = ((Integer)(eIt.next())).intValue();
						String id = ((Element)(((AElement)(aligned.elements[t].get(elementNumber))).element)).getAttribute("id");
						if (ids != "") {
							ids += " ";
						}
						ids += id;
					}
				}
				// ...
				if (t > 0) {
					xtargetsValue += ";";
				}
				xtargetsValue += ids;
			}
			// create link (alignment) info
			data = "<link id='...' xtargets='" + xtargetsValue + "'>\n";
			// output info
			try {
				out.write(data, 0, data.length());
			} catch (IOException e) {
				JOptionPane.showMessageDialog(
						null,
						"Can't write to file " + f.getName(),
						//"¤¤¤Title",
						"Error",   // 2006-09-21
						JOptionPane.ERROR_MESSAGE
				);
				//System.err.println("Exception when writing to " + f.getName() + ": ");
				//System.err.println(e.toString());
				ErrorMessage.error("Exception when writing to " + f.getName() + ":\n" + e.toString());   // 2006-08-10
				return;
			}
			
		}
		
		// create and output root end element
		
		data = "</linkGrp>\n";
		try {
			out.write(data, 0, data.length());
		} catch (IOException e) {
			JOptionPane.showMessageDialog(
					null,
					"Can't write to file " + f.getName(),
					//"¤¤¤Title",
					"Error",   // 2006-09-21
					JOptionPane.ERROR_MESSAGE
			);
			//System.err.println("Exception when writing to " + f.getName() + ": ");
			//System.err.println(e.toString());
			ErrorMessage.error("Exception when writing to " + f.getName() + ":\n" + e.toString());   // 2006-08-10
			return;
		}
		
		// close output file
		
		try {
			out.close();
		} catch (IOException e) {
			JOptionPane.showMessageDialog(
					null,
					"Can't close file " + f.getName(),
					//"¤¤¤Title",
					"Error",   // 2006-09-21
					JOptionPane.ERROR_MESSAGE
			);
			//System.err.println("Exception when closing " + f.getName() + ": ");
			//System.err.println(e.toString());
			ErrorMessage.error("Exception when closing " + f.getName() + ":\n" + e.toString());   // 2006-08-10
			return;
		}
		
	}
	
	// compute and display info about the current anchor word matches
	// and other matches §§§
	void computeMatches(AlignGui gui) {   // ### compute and display
		//void computeMatches() {
		//System.out.println("model sin computeMatches(). gui = " + gui);
		//gui.setMatchInfoTextArea(matchInfoDisplayable.toString());
		matchInfo.computeDisplayableList();
		gui.matchInfoList.setVisible(true);
	}
	
	// clear info about the current anchor word matches
	// and other matches §§§
	void clearMatches(AlignGui gui) {
		//matchInfoDisplayable.clear();
		//gui.setMatchInfoTextArea("");
		// ### earlier the info box was a JTextArea.
		// now it is a JList referring to a List.
		// it feels wrong to null the List.
		// instead we hide the box
		gui.matchInfoList.setVisible(false);
	}
	
	// 2006-02-23. log displayed info about the current anchor word matches and other matches §§§
	void logMatches(AlignGui gui) {
		//
		//System.out.println("Skal jeg skrive alignete elementer og match-info til loggfil?");
		
		if (gui.model.getLogging()) {   // 2006-04-18
			
			//System.out.println("Ja, jeg skal det.");
			
			try {
				
				// ###logMatches() er misvisende navn hvis også skal logge selve elementene
				
				String text = "";
				for (int t=0; t<Alignment.NUM_FILES; t++) {
					text += "Text " + t + "\n";
					for (Enumeration en = gui.model.toAlign.elements[t].elements(); en.hasMoreElements();) {
						AElement ae = (AElement)en.nextElement();
						int n = ae.elementNumber;
						text += "Element " + n + "\n";
						text += ae.toString() + "\n";
					}
				}
				text += "\n";
				
				//String text = "log - info:\n" + gui.model.matchInfo.displayableList.toString() + "\n";
				//String text = "";
				for (Enumeration en = gui.model.matchInfo.displayableList.elements(); en.hasMoreElements();) {
					text += (String)en.nextElement() + "\n";
				}
				text += "\n";
				//System.out.println(text);
				logFileOut.write(text, 0, text.length());
				
			} catch (Exception e) {
				
				//System.err.println("Exception when writing info to log file");   // ###(vet ikke om jeg har navnet på loggfilen)
				//System.err.println(e.toString());
				ErrorMessage.error("Exception when writing info to log file:\n" + e.toString());   // ###(vet ikke om jeg har navnet på loggfilen)   // 2006-08-10
				
			}
			
		}
		
	}
	
	// 2006-02-23. put warning in match info log
	//void logMatchesHeader(String header) {   // header or warning
	void logHeader(AlignGui gui, String header) {   // header or warning   // 2006-04-18
		//
		//System.out.println("Skal jeg skrive header eller advarsel til loggfil?");
		
		if (gui.model.getLogging()) {   // 2006-04-18
			
			//
			System.out.println("Ja, jeg skal det.");
			
			try {
				
				//String text = "log - warning: " + warning + "\n";
				String text = header + "\n\n";
				//System.out.println(text);
				logFileOut.write(text, 0, text.length());
				
			} catch (Exception e) {
				
				//System.err.println("Exception when writing header or warning to log file");   // ###(vet ikke om jeg har navnet på loggfilen)
				//System.err.println(e.toString());
				ErrorMessage.error("Exception when writing header or warning to log file:\n" + e.toString());   // 2006-08-10
				
			}
			
		}
		
	}
	
	/*
	 void unalign(AlignGui gui) {   // package access
	 toAlign.catch_(gui, aligned.drop(gui));
	 }
	 
	 void align(AlignGui gui) {   // package access
	 aligned.pickUp(gui, toAlign.flush(gui));
	 }
	 
	 void less(AlignGui gui, int t) {   // package access
	 unaligned.catch_(gui, t, toAlign.drop(gui, t));
	 }
	 
	 void more(AlignGui gui, int t) {   // package access
	 toAlign.pickUp(gui, t, unaligned.pop(gui, t));
	 }
	 */
	
	void unalign(AlignGui gui) {   // package access
		
		// 2006-02-23. put warning in match info log
		//System.out.println("unalign skal kalle logHeader() for å skrive advarsel til loggfil");
		//logMatchesHeader("*** Unalign operation - info above should have been erased ***");
		logHeader(gui, "*** Unalign operation - info above should have been erased ***");   // 2006-04-18
		
		//toAlign.catch_(aligned.drop());
		toAlign.catch_(aligned.drop(gui));
		
		computeMatches(gui);   // ### compute and display
		//ShowCompare.clear(gui);
		gui.compareInfoPanel.off();
		gui.compareInfoPanel.repaint();
	}
	
	void align(AlignGui gui, boolean scroll) {   // package access
		// needs to know gui to be able to scroll list boxes for finished alignments
		
		// 2006-02-23. log displayed info about the current anchor word matches and other matches §§§
		// 2006-02-23. put warning in match info log if more than one alignment
		if (gui.model.toAlign.pendingCount() > 1) {
			//logMatchesHeader("*** More than one alignment - info below is misleading ***");
			// boerre: ikke viktig
			logHeader(gui, "*** More than one alignment - info below is misleading ***");   // 2006-04-18
		} else {
			//logMatchesHeader("*** Next alignment ***");
			// boerre: ikke viktig
			logHeader(gui, "*** Next alignment ***");   // 2006-04-18
		}
		// Heller ikke viktig
		logMatches(gui);
		
		aligned.pickUp(gui, toAlign.flush(), scroll);
		
		computeMatches(gui);   // ### compute and display
		//ShowCompare.clear(gui);
		gui.compareInfoPanel.off();
		gui.compareInfoPanel.repaint();
		// garbage collect.
		// for each text find number of first element not yet aligned.
		// (if all are aligned the number will be one larger than the highest element number.)
		// (¤¤¤perhaps the code here should check the element numbers themselves and not just rely on size())
		int[] ix = new int[Alignment.NUM_FILES];
		for (int t=0; t<Alignment.NUM_FILES; t++) {
			ix[t] = gui.model.aligned.elements[t].size();
		}
		//System.out.println("align() - before garbage collect");
		gui.model.compare.garbageCollect(gui.model, ix);
	}
	
	// ¤¤¤ 2004-10-31. brukes ikke - i hvert fall ikke for øyeblikket
	void link(AlignGui gui, int t, int index, int elementNumber) {   // package access
		// ¤¤¤foreløpig
		//System.out.println("AlignmentModel sin link() med 4 param");
		toAlign.link(gui, t, index, elementNumber);
	}
	
	// ¤¤¤ 2004-10-31. bare denne brukes.
	// model sin link() kjøres kun når bruker klikker på et element i to-align
	void link(AlignGui gui, int t, int index, int elementNumber, int alignmentNumberToLinkTo) {   // package access
		// ¤¤¤foreløpig
		//System.out.println("AlignmentModel sin link() med 5 param");
		toAlign.link(gui, t, index, elementNumber, alignmentNumberToLinkTo);
	}
	
	/*
	* Author: Børre Gaup <boerre@skolelinux.no>, (C) 2006
	*
	* Copyright: See COPYING file that comes with this distribution
	*
	*/
	void suggest2() {
		String report = "";
		boolean outOfMemory = false;
		int runLimit = 0;
		int mode = Alignment.MODE_AUTO;
		if (!toAlign.empty()) {
			System.out.println("!toAlign.empty()");
			Toolkit.getDefaultToolkit().beep();
		} else {
			System.out.println("else !toAlign.empty()");
			mode = Alignment.MODE_AUTO;
			runLimit = 200;
		}
		int runCount = 0;
		boolean doneAligning = false;
		while (!doneAligning) {
			System.out.println("while !doneAligning");
			AlignmentModel.this.compare.resetBestPathScores();
			int[] position = new int[Alignment.NUM_FILES];
			for (int t=0; t<Alignment.NUM_FILES; t++) {
				if (((DefaultListModel)(unaligned.elements[t])).size() > 0) {
					position[t] = ((AElement)(((DefaultListModel)(unaligned.elements[t])).get(0))).elementNumber - 1;   // #############
				} else {
					position[t] = AlignmentModel.this.nodes[t].getLength() - 1;
				}
			}
			QueueList queueList = new QueueList(AlignmentModel.this, position);
			QueueList nextQueueList;
			PathStep step;
			int stepCount = 0;
			boolean doneLengthening = false;
			do {
				System.out.println("inne i do");
				Iterator qIt = queueList.entry.iterator();
				nextQueueList = new QueueList();
				while (qIt.hasNext()) {
					System.out.println("while (qIt.hasNext())");
					Object temp = qIt.next();
					QueueEntry queueEntry = (QueueEntry)(temp);
					if (!queueEntry.removed) {
						if (queueEntry.end) {
							System.out.println("if (!queueEntry.removed)");
							QueueEntry newQueueEntry = (QueueEntry)queueEntry.clone();
						} else {
							Iterator iIt = AlignmentModel.this.compare.stepList.iterator();
							while (iIt.hasNext()) {
								System.out.println("while (iIt.hasNext())");
								System.out.println(runCount);
								step = (PathStep)iIt.next();
								try {
									QueueEntry newQueueEntry = queueEntry.makeLongerPath(AlignmentModel.this, step);
									if (newQueueEntry.path != null) {
										int[] pos = newQueueEntry.path.position;
										nextQueueList.remove(pos);   
										queueList.remove(pos);
										nextQueueList.add(newQueueEntry);
									}
								} catch (EndOfAllTextsException e) {
									QueueEntry newQueueEntry = (QueueEntry)queueEntry.clone();
									newQueueEntry.end = true;
									if (!nextQueueList.contains(newQueueEntry)) {
										nextQueueList.add(newQueueEntry);
									}
								} catch (EndOfTextException e) {
								} catch (BlockedException e) {
									//...
								}
							}
						}
					}
				}
				nextQueueList.removeForReal();
				if (nextQueueList.empty()) {
					doneLengthening = true;
				} else {
					queueList = nextQueueList;
					stepCount++;
					doneLengthening = (stepCount >= AlignmentModel.this.getMaxPathLength());
				}
			} while (!doneLengthening);
				
			if ((queueList.entry.size() == 0) || ((queueList.entry.size() == 1) 
				&& (((QueueEntry)(queueList.entry.get(0))).path.steps.size() == 0)))
			{
				doneAligning = true;
			} else {
				System.out.println("4060 ");
				Iterator qIt2 = queueList.entry.iterator();
				float normalizedBestScore = AlignmentModel.BEST_PATH_SCORE_NOT_CALCULATED;
				Path bestPath = null;
				while (qIt2.hasNext()) {
					System.out.println("while (qIt2.hasNext())");
					QueueEntry candidate = ((QueueEntry)qIt2.next());
					float normalizedCandidateScore = candidate.score / candidate.path.getLengthInSentences();
					report += "normalized score: " + normalizedCandidateScore + "\n"; 
					if (normalizedCandidateScore > normalizedBestScore) {
						normalizedBestScore = normalizedCandidateScore;
						bestPath = candidate.path;
					}
				}
// 				System.out.println(">>>=================>>> bestScore = " + bestScore);
				System.out.println(">>>=================>>> best path = " + bestPath);
				if (bestPath.steps.size() > 0) {
					PathStep stepSuggestion = (PathStep)bestPath.steps.get(0);
					System.out.println(">>>=================>>> suggested step = " + stepSuggestion);
					for (int t=0; t<Alignment.NUM_FILES; t++) {
						for (int i=0; i<stepSuggestion.increment[t]; i++) {
// 							more(gui, t);
							toAlign.pickUp(t, unaligned.pop(t));
						}
					}
					runCount++;
					if (mode == Alignment.MODE_ONE) {
						doneAligning = true;
					} else if (mode == Alignment.MODE_AUTO) {
						if (runCount < runLimit) {
							System.out.println(runLimit);
							doneAligning = false;
						} else {
							doneAligning = true;
						}
					} else if (mode == Alignment.MODE_SKIP11) {
						if ((runCount < runLimit) && stepSuggestion.is11()) {
							doneAligning = false;
						} else {
							doneAligning = true;
						}
					}
					if (!doneAligning) {
						//gui.model.align(gui, false);
						System.out.println("!doneAligning");
					}
					
					System.out.println("rapport fra utvelgelsesprosessen:\n" + report);
				} else {
					Toolkit.getDefaultToolkit().beep();
					System.out.println("No more unaligned text");
					doneAligning = true;
					
				}
				
			}
				
			if (!doneAligning) {
				for (int t=0; t<Alignment.NUM_FILES; t++) {
				}
			}
			System.out.println("MemTest.getRemainingHeap()=" + MemTest.getRemainingHeap());
			if (MemTest.getMemoryPercentUsed() > 90) {
				// reset the memory usage
				doneAligning = true;
				outOfMemory = true;
			}
			
		}
		System.gc();
		saveFilesAutomatically();
	}


	// ### ikke godt navn? i skip-1-1-modus og automatisk gjør den mer enn bare å foreslå
	void suggest(AlignGui gui) {   // package access
		//void suggest() {   // package access
		
		// debug
		//int debugCount = 0;
		
		boolean outOfMemory = false;   // 2006-10-03
		
		//System.out.println("model sin suggest(). gui = " + gui);
		//System.out.println("model sin suggest()");
		if (!toAlign.empty()) {
			// automatically align before suggesting
			//System.out.println("automatically align before suggesting");
			gui.model.align(gui, false);   // false = don't scroll aligned yet (because of a memory leak - ?)
		}
		if (!toAlign.empty()) {
			Toolkit.getDefaultToolkit().beep();
			//System.out.println("BEEEEEEEEEEEEEEEEP AlignmentModel suggest");
			//System.out.println("Program error? Alignment of pending elements failed?");
		} else {
			
			//System.out.println("else");
			//boolean skip11 = gui.skip11CheckBox.isSelected();
			int mode = gui.getMode();   // ######### heller hete runMode? for ikke å få sammenblanding med newline ancestor-mode?
			//##############må forbedres
			//int skip11Limit;
			int runLimit;
			try {
				//skip11Limit = Integer.parseInt(gui.skip11LimitTextField.getText());
				runLimit = Integer.parseInt(gui.runLimitTextField.getText());
			} catch (NumberFormatException e) {
				//skip11Limit = 999999;   // ###
				runLimit = 999999;   // ###
			}
			//int skip11count = 0;
			int runCount = 0;
			boolean doneAligning = false;
			// loop. do one alignment, or perhaps several alignments (if skip 1-1)
			//System.out.println("before while (!doneAligning)");
			while (!doneAligning) {
				
				//System.out.println("in while (!doneAligning)");
				//AlignmentModel.this.compare.testPrint(AlignmentModel.this);
				
				AlignmentModel.this.compare.resetBestPathScores();   // ¤¤¤¤¤¤¤¤¤¤¤ nå eller etterpå?
				
				/*
				 // ¤¤¤ foreløpig.
				  // foreslår ett element fra hver tekst
				   for (int t=0; t<Alignment.NUM_FILES; t++) {
				   toAlign.pickUp(t, unaligned.pop(t));
				   }
				   */
				
				/*
				 // ¤¤¤ foreløpig.
				  int opt_n = -1;   // ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
				  OperationNode test = GaleChurch.testGaleChurch(0, 1, unaligned.elements[0], unaligned.elements[1], opt_n);
				  //System.out.println("G&C har bestemt operation " + test.operation);
				   */
				
				//System.out.println("!!! suggest");
				// start 1 before first unaligned elements ¤¤¤
				int[] position = new int[Alignment.NUM_FILES];
				for (int t=0; t<Alignment.NUM_FILES; t++) {
					//System.out.println("t = " + t);
					if (((DefaultListModel)(unaligned.elements[t])).size() > 0) {
						//System.out.println("DefaultListModel)(unaligned.elements[t])).size() > 0");
						position[t] = ((AElement)(((DefaultListModel)(unaligned.elements[t])).get(0))).elementNumber - 1;   // #############
					} else {
						// no more unaligned elements in text t.
						// ### er dette suspekt? kan det hende at ikke alle elementene fra DOM er med???
						//System.out.println("no more unaligned elements in text t");
						position[t] = AlignmentModel.this.nodes[t].getLength() - 1;
					}
				}
                                /*
				System.out.println("!!! skal lage ny QueueList. position=" + position[0] + "," + position[1] + ". altså - dette er den siste cellen som vi har alignet, ikke den første vi skal aligne");
				will investigate "all" possible paths with a certain number of steps.
				will loop once per step, each time building "all" paths
				that are one step longer than in the previous loop.
				collect the paths in the queue list.
				init queue list (queueList)
                                */
				QueueList queueList = new QueueList(AlignmentModel.this, position);
				// the paths that are one step longer will, while they are being created,
				// reside in nextQueueList
				QueueList nextQueueList;
				// variable for each of all the possible steps to try when lengthening a path: 0-0, 0-1, etc
				PathStep step;
				// init counter for the lengthening loop
				int stepCount = 0;
				// the lengthening loop
				boolean doneLengthening = false;
				//System.out.println("before do ... while (!doneLengthening)");
				do {
					/*
					 debugCount++;
					 if (debugCount <= 3) {
					 System.out.println("\n>>>>>>>>>>>>>> queueList =\n" + queueList + "\n");
					 }
					 */
					//System.out.println("\nstepCount=" + stepCount + "\n");
					//System.out.println("\nqueueList before lengthening = " + queueList + "\n");
					Iterator qIt = queueList.entry.iterator();
					nextQueueList = new QueueList();
					// loop over each entry in the queue list. each entry is a path
					//System.out.println("before while (qIt.hasNext())");
					while (qIt.hasNext()) {
						//System.out.println("in while (qIt.hasNext())");
						//System.out.println("inner while");
						Object temp = qIt.next();
						////System.out.println("crocodile");
						QueueEntry queueEntry = (QueueEntry)(temp);
						//System.out.println("before if (!queueEntry.removed)");
						if (!queueEntry.removed) {   // ### 2005-11-02. hmmm. denne var det ikke så mye vits så lenge jeg ikke merket for fjerning i queueList, bare i nextQueueList
							if (queueEntry.end) {
								// path goes to the end of all texts.
								// use as it is
								QueueEntry newQueueEntry = (QueueEntry)queueEntry.clone();   // denne har allerede newQueueEntry.end = true;
							} else {
								//System.out.println("in if (!queueEntry.removed)");
								//System.out.println("queueEntry = " + queueEntry);
								////System.out.println("AlignmentModel.this.compare.incrementsList.size() = " + AlignmentModel.this.compare.incrementsList.size());
								// loop through all the possible steps to lengthen the current path with.
								// note. some or all of these steps will not be possible after all
								// at the end of the texts
								Iterator iIt = AlignmentModel.this.compare.stepList.iterator();
								//System.out.println("before while (iIt.hasNext())");
								while (iIt.hasNext()) {
									//System.out.println("in while (iIt.hasNext())");
									step = (PathStep)iIt.next();
									//System.out.println("*** neste steg å prøve å forlenge med er " + step);
									//nextQueueList.entry.add(new QueueEntry(AlignmentModel.this, queueEntry, step));
									//QueueEntry newQueueEntry = new QueueEntry(AlignmentModel.this, queueEntry, step);
									try {
										//System.out.println("before makeLongerPath(...");
										QueueEntry newQueueEntry = queueEntry.makeLongerPath(AlignmentModel.this, step);
										//System.out.println("after makeLongerPath(...");
										//System.out.println("after makeLongerPath(... newQueueEntry = " + newQueueEntry);
										if (newQueueEntry.path != null) {   // ¤¤¤ .path = null er min krøkkete måte å fortelle at det nye forslaget til path ikke er bedre enn andre paths til samme position, og at forslaget skal kastes
											//System.out.println("forlenget path beste hittil");
											// this new path might be a better (better-scoring) path than
											// some other paths in the new list. remove those other paths, if any
											int[] pos = newQueueEntry.path.position;
											//if ((pos[0] == 2) && (pos[1] == 3)) {
											//	debugCount++;
											//	if (debugCount == 2) {
											//		System.out.println("\n>> >> >> 1 queueList = " + queueList + "\n");
											//	}
											//}
											nextQueueList.remove(pos);   // doesn't remove them for real. just marks them for removal later
											//if ((pos[0] == 2) && (pos[1] == 3)) {
											//	if (debugCount == 2) {
											//		System.out.println("\n>> >> >> 2 queueList = " + queueList + "\n");
											//	}
											//}
											queueList.remove(pos);   // must do the same thing in the source. (###dodgy?) see comments for the QueueList remove() method
											//if ((pos[0] == 2) && (pos[1] == 3)) {
											//	if (debugCount == 2) {
											//		System.out.println("\n>> >> >> 3 queueList = " + queueList + "\n");
											//	}
											//}
											// insert new path in the new list
											nextQueueList.add(newQueueEntry);
										} else {
											//System.out.println("forlenget path skåret ikke høyt nok");
										}
									} catch (EndOfAllTextsException e) {
										//System.out.println("suggest() catches EndOfAllTextsException");
										// end of all texts.
										// use path as it is, but mark it properly
										QueueEntry newQueueEntry = (QueueEntry)queueEntry.clone();
										newQueueEntry.end = true;
										//System.out.println("suggest() made newQueueEntry = " + newQueueEntry);
										// insert new path in the new list unless already there.
										if (!nextQueueList.contains(newQueueEntry)) {
											nextQueueList.add(newQueueEntry);
										}
									} catch (EndOfTextException e) {
										//System.out.println("suggest() catches EndOfTextException");
										// end of at least one text but not all of them.
										// forget
										//System.out.println("EndOfTextException");
									} catch (BlockedException e) {
										//...
									}
								}
							}
						}
					}
					//System.out.println("\n>>>>>>>>>>>nextQueueList før removeForReal = " + nextQueueList);
					nextQueueList.removeForReal();   // remove for real. see above
					//System.out.println("\n>>>>>>>>>>>nextQueueList etter removeForReal = " + nextQueueList);
					if (nextQueueList.empty()) {
						// not possible to lengthen path. must have reached the end of all the texts
						doneLengthening = true;
					} else {
						queueList = nextQueueList;
						//System.out.println("queueList after lengthening = " + queueList);
						stepCount++;
						doneLengthening = (stepCount >= AlignmentModel.this.getMaxPathLength());
					}
				} while (!doneLengthening);
				//System.out.println("!!! har laget ny QueueList med alle stier som har <= " + stepCount + " steg. queueList = " + queueList + "\n");
				//System.out.println("!!! Skal finne den beste stien av disse");
				
				// ...
				
				if (   (queueList.entry.size() == 0)   // ### will not happen?
						|| (   (queueList.entry.size() == 1)
								&& (((QueueEntry)(queueList.entry.get(0))).path.steps.size() == 0)
						)
				)
				{
					
					// must be end of all texts
					
					doneAligning = true;
					
				} else {
					
					Iterator qIt2 = queueList.entry.iterator();
					//float bestScore = -1.f;   // ###
					// normalized = diveded by number of sentences.
					// done because: the paths compared may well have the same number of steps,
					// but they often have a different number of sentences.
					// if not normalized a path with e.g 2-1 + 1-2 can win over a correct 1-1 + 1-1 + 1-1
					// because it gains extra points from the extra sentences the former path has at its end
					//float normalizedBestScore = -1.f;   // ###
					float normalizedBestScore = AlignmentModel.BEST_PATH_SCORE_NOT_CALCULATED;   // 2006-09-20
					Path bestPath = null;
					//String report = ""; //%%%
					while (qIt2.hasNext()) {
						QueueEntry candidate = ((QueueEntry)qIt2.next());
						//System.out.println("!!! candidate.score = " + candidate.score);
						//report += "---------------------" + "\n"; //%%%
						//report += "path: " + candidate.path + "\n"; //%%%
						//report += "score: " + candidate.score + "\n"; //%%%
						//report += "length in sentences: " + candidate.path.getLengthInSentences() + "\n"; //%%%
						float normalizedCandidateScore = candidate.score / candidate.path.getLengthInSentences();
						//report += "normalized score: " + normalizedCandidateScore + "\n"; //%%%
						//if (candidate.score > bestScore) {
						if (normalizedCandidateScore > normalizedBestScore) {
							//System.out.println("!!! bedre enn bestScore = " + bestScore);
							//bestScore = candidate.score;
							normalizedBestScore = normalizedCandidateScore;
							bestPath = candidate.path;
						}
					}
					//System.out.println(">>>=================>>> bestScore = " + bestScore);
					//System.out.println(">>>=================>>> best path = " + bestPath);
					
					// ...
					if (bestPath.steps.size() > 0) {
						
						//System.out.print("A ");
						//MemTest.print("Tenured Gen", "");
						
						PathStep stepSuggestion = (PathStep)bestPath.steps.get(0);
						//System.out.println(">>>=================>>> suggested step = " + stepSuggestion);
						
						//System.out.print("B ");
						//MemTest.print("Tenured Gen", "");
						
						// ...
						for (int t=0; t<Alignment.NUM_FILES; t++) {
							for (int i=0; i<stepSuggestion.increment[t]; i++) {
								more(gui, t);
							}
						}
						
						//System.out.print("C ");
						//MemTest.print("Tenured Gen", "");
						
						// ... present anchor match info in gui ??????????????????????????
						
						// ...
						//skip11count++;
						runCount++;
						//doneAligning = !(skip11 && (skip11count < skip11Limit) && stepSuggestion.is11());
						if (mode == Alignment.MODE_ONE) {
							doneAligning = true;
						} else if (mode == Alignment.MODE_AUTO) {
							if (runCount < runLimit) {
								doneAligning = false;
							} else {
								doneAligning = true;
							}
						} else if (mode == Alignment.MODE_SKIP11) {
							if ((runCount < runLimit) && stepSuggestion.is11()) {
								doneAligning = false;
							} else {
								doneAligning = true;
							}
						}
						if (!doneAligning) {
							// skip mode. automatically align what was suggested
							// ### er det mulig å kjøre en action uten å gå via en komponent?
							////gui.alignButton.processMouseMotionEvent((MouseEvent)(java.awt.event.MouseEvent.MOUSE_CLICKED));   // ?????????????????????
							/////gui.AlignAction.actionPerformed(new ActionEvent(...));
							//### gui.model kunstig? heller AlignmentModel.this??
							gui.model.align(gui, false);   // false = don't scroll aligned yet (because of a memory leak - ?)
						}
						
						/*
						 //debug
						  if (doneAligning) {
						  System.out.println("we are doneAligning. compare er nå\n" + gui.model.compare);
						  System.out.println("rapport fra utvelgelsesprosessen:\n" + report);
						  }
						  */
						
						//System.out.print("D ");
						//MemTest.print("Tenured Gen", "");
						
					} else {
						
						// no best path. reason: no more unaligned text
						
						Toolkit.getDefaultToolkit().beep();
						System.out.println("No more unaligned text");
						doneAligning = true;
						
					}
					
				}
				
				if (!doneAligning) {
					// force painting if doing more than one alignment
					// (in skip 1-1 or automatic mode)
					
					//System.out.print("E ");
					//MemTest.print("Tenured Gen", "");
					
					for (int t=0; t<Alignment.NUM_FILES; t++) {
						
						// paintImmediately
						// public void paintImmediately(int x, int y, int w, int h)
						// Paints the specified region in this component
						// and all of its descendants that overlap the region, immediately.
						// It's rarely necessary to call this method.
						// In most cases it's more efficient to call repaint,
						// which defers the actual painting
						// and can collapse redundant requests into a single paint call.
						// This method is useful if one needs to update the display
						// while the current event is being dispatched
						
						// #### dette funker, med forbehold:
						// aligned ruller ikke
						// og toAlign står tom.
						// og så tar det tid mellom tekst 1 og 2
						//gui.alignedListBox[t].paintImmediately(gui.alignedListBox[t].getBounds());
						//gui.toAlignListBox[t].paintImmediately(gui.toAlignListBox[t].getBounds());
						//gui.unalignedListBox[t].paintImmediately(gui.unalignedListBox[t].getBounds());
						
						// ### med dette så friskes bare en av rutene opp - første align.
						// og oppfrisk starter litt langt nede. må være noe med koord.
						//gui.alignedScrollPane[t].paintImmediately(gui.alignedScrollPane[t].getBounds());
						//gui.toAlignScrollPane[t].paintImmediately(gui.toAlignScrollPane[t].getBounds());
						//gui.unalignedScrollPane[t].paintImmediately(gui.unalignedScrollPane[t].getBounds());
						
						// ### aha. getBounds er feil. det skal være getSize el. likn
						
						//System.out.print("F. t=" + t + ". ");
						//MemTest.print("Tenured Gen", "");
						
						// ### aha. mangler scroll. har utsatt scroll pga mulig memory leak.
						// se andre steder i koden. aktiverer scroll igjen, og ser hvordan det går
						// paint
						// ###vis først at element forsvant fra unaligned
						// 2006-10-03. PRØVER UTEN DENNE
						//gui.unalignedScrollPane[t].paintImmediately(0, 0, gui.unalignedScrollPane[t].getWidth(), gui.unalignedScrollPane[t].getHeight());
						// ###ikke vits i å vise toAlign? kanskje for å sikre at den blir blanket ut i det tilfellet at det står noe der fra før.
						// dersom forslaget skal vise, må det skje et annet sted i programmet
						
						//System.out.print("G ");
						//MemTest.print("Tenured Gen", "");
						
						// 2006-10-03. PRØVER UTEN DENNE
						//gui.alignedScrollPane[t].paintImmediately(0, 0, gui.alignedScrollPane[t].getWidth(), gui.alignedScrollPane[t].getHeight());
						// ###så at det dukker opp i aligned
						
						//System.out.print("H ");
						//MemTest.print("Tenured Gen", "");
						
						// 2006-10-03. PRØVER UTEN DENNE
						//gui.toAlignScrollPane[t].paintImmediately(0, 0, gui.toAlignScrollPane[t].getWidth(), gui.toAlignScrollPane[t].getHeight());
						
						//System.out.print("I ");
						//MemTest.print("Tenured Gen", "");
						
						// hjelpe på memoryproblem? ????????????????????? næh
						//Thread.yield();
						
					}
				}
				
				// 2006-10-03
				//System.out.println("MemTest.getRemainingHeap()=" + MemTest.getRemainingHeap));
				if (MemTest.getMemoryPercentUsed() > 90) {
					// reset the memory usage
					doneAligning = true;
					outOfMemory = true;
				}
				// end 2006-10-03
				
			}
			
			
			
			// 2006-10-03. painter BARE NÅ TIL SLUTT. dette hjalp mot vanvittig minnebruk når man kjørte automatisk
			/*
			for (int t=0; t<Alignment.NUM_FILES; t++) {
				gui.unalignedScrollPane[t].paintImmediately(0, 0, gui.unalignedScrollPane[t].getWidth(), gui.unalignedScrollPane[t].getHeight());
				gui.alignedScrollPane[t].paintImmediately(0, 0, gui.alignedScrollPane[t].getWidth(), gui.alignedScrollPane[t].getHeight());
				gui.toAlignScrollPane[t].paintImmediately(0, 0, gui.toAlignScrollPane[t].getWidth(), gui.toAlignScrollPane[t].getHeight());
			}
			*/
			
			
			
			
			
			
			
			// ... present anchor match info in gui
			//clearAnchorWordMatches();   // ############################# andre steder
// 			computeMatches(gui);   // ### compute and display
			
			//System.out.println("J");
			//MemTest.print("Tenured Gen", "");
			
			//ShowCompare.show(gui);
// 			gui.compareInfoPanel.on();
			
			//System.out.println("K");
			//MemTest.print("Tenured Gen", "");
			
// 			gui.compareInfoPanel.repaint();
			
			//System.out.println("L");
			//MemTest.print("Tenured Gen", "");
			
		}
		
		// scroll aligned. (waited until now because of a memory leak - ?)

		for (int t=0; t<Alignment.NUM_FILES; t++) {
			gui.alignedListBox[t].ensureIndexIsVisible(aligned.elements[t].getSize()-1);
		}
		
		// 2006-10-05
		// try to get java to do garbage collection.
		// ###System.runFinalization() eller System.gc() hjalp kanskje litt.
		// minnebruk gikk slik:
		//           /
		//        /\/
		//     /\/
		//  /\/
		// /
		// i stedet for slik:
		//     /
		//    /
		//   /
		//  /
		// /
		// man bare inntil programmet slapp opp for minne første gang!
		//System.runFinalization();
		System.gc();
		// end 2006-10-05
		/*
		if (outOfMemory) {
                        saveFilesAutomatically(gui);
		}*/
		
	}

        void saveFilesAutomatically() {
                /*  Make the program save the result automatically, and then continue 
                    *
                    * Below we compute the externalFormatName. At the same time we 
                    * use the intermediate results of this to save the _cor.xml
                    * and _new.txt files
                    */

                File tempPath;
                String outputFilenameSuggestion;
                File outputFilepathSuggestion;
                String tempFilename;
                String tempFilepath;

                outputFilenameSuggestion = "";
                tempFilepath = "";

                for(int t=0; t<Alignment.NUM_FILES; t++) {
                        tempPath = new File(inputFilepath[t]);
                        tempFilename = tempPath.getName();
                        tempFilepath = tempPath.getParent();
                        
                        if (outputFilenameSuggestion != "") {
                                outputFilenameSuggestion += "_";
                        }
                        String baseName = ExtensionUtils.getFilenameWithoutExtension(tempFilename);
                        outputFilenameSuggestion += baseName;
                        
                        outputFilepathSuggestion = new File(tempFilepath + "/" + ExtensionUtils.getFilenameWithoutExtension(tempFilename) + "_cor.xml");
                        saveCorrespFormatFile(outputFilepathSuggestion, t, getCharset(t));
                        outputFilepathSuggestion = new File(tempFilepath + "/" + ExtensionUtils.getFilenameWithoutExtension(tempFilename) + "_new.txt");
                        saveNewlineFormatFile(outputFilepathSuggestion, t, getCharset(t), getAncestorFilter());
                }

                outputFilenameSuggestion += ".xml";
                outputFilepathSuggestion = new File(tempFilepath + "/" + outputFilenameSuggestion);
                saveExternalFormatFile(outputFilepathSuggestion);

                System.out.println("Restarted ...");
        }

	void skipCorresp(AlignGui gui) {
		
		// the texts may contain some alignments already - in the form of corresp attributes.
		// if so, link them together, according to the corresp values,
		// and get them up into aligned area,
		// until we run out of matching corresp values.
		// ### to-align must be empty and the starts of unaligned must be in synch
		
		// ##### hadde vært tøft om de viste med farger allerede i unaligned????????????
		
		System.out.println("enter skipCorresp()");
		
		// is to-align empty?
		if (!toAlign.empty()) {
			// no. ######### trenger bedre advarsel/forklaring for brukeren enn kun et pip
			Toolkit.getDefaultToolkit().beep();
			System.out.println("Can't do this while there are pending alignments");
		} else {
			
			System.out.println("to-align is empty");
			AElement aEl;
			Element element;
			AlignmentsEtc someAligned;
			int alignmentNumber = 0;
			int t, tt, t2;
			Link link;
			
			int holeyCount = 0;
			
			// for each text continue as long as there are elements,
			// and the elements have ''corresp'' attributes.
			// initialize the thing that keeps track of which texts
			// have run out of relevant elements.
			boolean[] stop = new boolean[Alignment.NUM_FILES];
			for (t=0; t<Alignment.NUM_FILES; t++) {
				stop[t] = false;
			}
			
			// for each
			
			// make a new empty someAligned.
			// will be filled and emptied and reused. ###thrown away
			someAligned = new AlignmentsEtc();
			//System.out.println("make a new empty someAligned");
			
			// ...
			
			boolean done = false;
			do {
				
				System.out.println("find the elements belonging to the next alignment, i.e, find the corresponding elements in each text, ...");
				// find the elements belonging to the next alignment,
				// i.e, find the corresponding elements in each text,
				// from the unaligned areas.
				// what about loners?
				// all right. they might need special treatment.
				// what about crossing relations?
				// oh yes. if relations cross we have to continue and find more alignments,
				// until they make a continuous glob without any holes
				
				System.out.println("first skim off all loners lying topmost in the unaligned.");
				// first skim off all loners lying topmost in the unaligned.
				// take them from the left
				
				for (t=0; t<Alignment.NUM_FILES; t++) {
					
					if (!stop[t]) {
						
						tt = 1 - t;   // ###
						System.out.println("text " + t + ". other text is " + tt);
						
						System.out.println("loop just in case there are several loners in a row in the same text");
						// loop just in case there are several loners in a row in the same text
						boolean finishedLonersInThisText = false;
						do {
							
							System.out.println("try to get loner from text " + t);
							// try to get loner from text t.
							// stop text t if no element or no corresp
							
							if (unaligned.elements[t].size() == 0) {
								
								System.out.println("no more unaligned elements in text " + t + ". stop text " + t);
								stop[t] = true;
								finishedLonersInThisText = true;
								
							} else {
								
								System.out.println("1 get next available element in text " + t);
								// get next available element in text t #########
								//element = (Element)((AElement)(unaligned.elements[t].get(0))).element;
								aEl = Skip.getNextAvailableUnalignedElement(unaligned, someAligned, t);
								System.out.println("1");
								element = (Element)aEl.element;
								System.out.println("2");
								System.out.println(element.getAttributes());
								System.out.println(element.getAttributes().getNamedItem("corresp"));
								// 2006-09-15 ###dupl. kode
								String correspValue;   // 2006-09-15
								if (element.getAttributes().getNamedItem("corresp") == null) {   // 2006-09-15
									correspValue = "";   // 2006-09-15
								} else {   // 2006-09-15
									//String correspValue = element.getAttributes().getNamedItem("corresp").getNodeValue();
									correspValue = element.getAttributes().getNamedItem("corresp").getNodeValue();   // 2006-09-15
								}   // 2006-09-15
								System.out.println("topmost unaligned element 1. correspValue = " + correspValue);
								if (correspValue == "") {
									
									System.out.println("(I) element with no corresp attribute. stop text " + t);
									// element with no corresp attribute
									stop[t] = true;
									finishedLonersInThisText = true;
									
								} else {
									
									System.out.println("there are id(s) in the corresp attribute of the element");
									// there are id(s) in the corresp attribute of the element
									String[] correspIds = correspValue.split(" ");
									if (correspIds.length > 1) {
										System.out.println("more than one id. this element can't be a loner.");
										// more than one id. this element can't be a loner.
										// loners refer to one element only, on a "parent" level
										finishedLonersInThisText = true;
									} else {
										System.out.println("one id. check alignable elements in the other text to see if the id belongs to one of them");
										// one id. check alignable elements in the other text
										// to see if the id belongs to one of them
										if (XmlTools.getElementByIdInNodeList(nodes[tt], correspValue) != null) {
											System.out.println("belongs to alignable element in other text. => not loner");
											// belongs to alignable element in other text.
											// => not loner
											// check further
											if (XmlTools.getElementByIdInDefaultListModel(unaligned.elements[tt], correspValue) != null) {
												// the element in the other text is an unaligned element
												// ok
												finishedLonersInThisText = true;
											} else {
												System.out.println("error in corresp. treat as loner");
												// the element in the other text is not an unaligned element
												// error in corresp
												//###############
												// treat as loner
											}
										} else if(XmlTools.getElementByIdInNodeList(allNodes[tt], correspValue) != null) {
											System.out.println("belongs to other element in other text, presumably one on a 'parent' level. => loner");
											// belongs to other element in other text,
											// presumably one on a "parent" level.
											// (¤¤¤but we don't check that element further,.
											// neither its "level" nor its location)
											// => loner
										} else {
											System.out.println("error in file. treat as loner");
											// error in file.
											//###############
											// treat as loner
										}
									}
									
									// ...
									System.out.println("xxx");
									
									if (!finishedLonersInThisText) {
										System.out.println("found loner. pop it from unaligned and make an alignment out of it");
										// found loner.
										//// pop it from unaligned and make an alignment out of it
										// make an alignment out of it
										//AElement aEl = (AElement)(AlignmentModel.this.unaligned.pop(t));
										System.out.println("1.5 get next available element in text " + t);
										// get next available element in text t #########
										//AElement aEl = (AElement)(unaligned.elements[t].get(0));
										aEl = Skip.getNextAvailableUnalignedElement(unaligned, someAligned, t);
										link = new Link();
										link.alignmentNumber = alignmentNumber;
										aEl.alignmentNumber = link.alignmentNumber;
										alignmentNumber++;
										link.elementNumbers[t] = new TreeSet<Integer>();
										link.elementNumbers[t].add(aEl.elementNumber);
										link.elementNumbers[tt] = new TreeSet<Integer>();
										// add it to our collection of ... alignments (the someAligned thing)
										someAligned.add(link);
										//someAligned.print();
										//if (someAligned.alignments.size() > 3) {
										//	System.out.println("kill this process"); stop[0] = true; stop[1] = true; finishedLonersInThisText = true;
										//}
										// ###also the element. kunne ikke Link også holdt rede på disse?
										//someAligned.add(t, element);
										someAligned.add(t, aEl);
										//someAligned.print();
										if (!someAligned.hasHoles()) {
											// got one or more alignments, with no holes.
											// pop the relevant elements out of unaligned.
											// we don't need their content.
											// we got all the data we need already.
											// just throw them away
											System.out.println("pop and throw them away 1");
											for (t2=0; t2<Alignment.NUM_FILES; t2++) {
												for (int i=0; i<((DefaultListModel)someAligned.elements[t2]).size(); i++) {
													System.out.println("pops from text " + t2);
													AElement aDum = (AElement)(AlignmentModel.this.unaligned.pop(t2));
													String id = aDum.element.getAttributes().getNamedItem("id").getNodeValue();
													System.out.println("popped element with id " + id + " from text " + t2);
													///////////MemTest.print("Heap memory");
													//System.out.println("A");
													//MemTest.print("Tenured Gen", "");
													//System.out.println("unaligned.elements[" + t2+ "].size() = " + unaligned.elements[t2].size());
												}
											}
											// process the someAligned and empty it
											System.out.println("process the someAligned and empty it 1");
											toAlign.catch_(someAligned);
											someAligned = new AlignmentsEtc();
											aligned.pickUp(gui, toAlign.flush(), false);   // false = don't scroll aligned yet (because of a memory leak - ?)
											holeyCount = 0;
										} else {
											holeyCount++;
											System.out.println("holes 1");
										}
									}
									
								}
							}
						} while (!finishedLonersInThisText);
						
					}
					
				} // for
				
				/// then ...
				System.out.println("finished skimming loners");
				
				if (!(stop[0] && stop[1])) {   // ###
					
					// not run out of elements with corresp
					
					System.out.println("grab the leftmost topmost element");
					// grab the leftmost topmost element
					// of a more substantial alignment (i.e, not loner)
					
					link = new Link();
					link.alignmentNumber = alignmentNumber;
					alignmentNumber++;
					t = 0;
					tt = 1;
					System.out.println("get top element in text " + t);
					// get top element in text t.
					// stop text t if no element or no corresp
					if (unaligned.elements[t].size() == 0) {
						
						System.out.println("no more unaligned elements in text " + t + ". stop text " + t);
						
						stop[t] = true;
						
					} else {
						
						System.out.println("2 get next available element in text " + t);
						// get next available element in text t #########
						//AElement aEl = (AElement)(unaligned.elements[t].get(0));
						aEl = Skip.getNextAvailableUnalignedElement(unaligned, someAligned, t);
						element = (Element)aEl.element;
						// 2006-09-15 ###dupl. kode
						String correspValue;   // 2006-09-15
						if (element.getAttributes().getNamedItem("corresp") == null) {   // 2006-09-15
							correspValue = "";   // 2006-09-15
						} else {   // 2006-09-15
							//String correspValue = element.getAttributes().getNamedItem("corresp").getNodeValue();
							correspValue = element.getAttributes().getNamedItem("corresp").getNodeValue();   // 2006-09-15
						}   // 2006-09-15
						System.out.println("topmost unaligned element 2. correspValue = " + correspValue);
						if (correspValue == "") {
							
							System.out.println("(II) element with no corresp attribute. stop text " + t);
							// element with no corresp attribute
							stop[t] = true;
							
						} else {
							
							System.out.println("there are id(s) in the corresp attribute of the element");
							// there are id(s) in the corresp attribute of the element
							//link.alignmentNumber = alignmentNumber;
							link.elementNumbers[t] = new TreeSet<Integer>();
							link.elementNumbers[t].add(aEl.elementNumber);
							link.elementNumbers[tt] = new TreeSet<Integer>();
							// ###also ...
							//someAligned.add(t, element);
							aEl.alignmentNumber = link.alignmentNumber;
							someAligned.add(t, aEl);
							//someAligned.print();
							System.out.println("get all the corresponding elements in the other text.");
							// get all the corresponding elements in the other text.
							String[] correspIds = correspValue.split(" ");
							AElement otherAEl = null;
							for (int i = 0; i < correspIds.length; i++) {
								System.out.println("get Node otherEl");
								Node otherEl = XmlTools.getElementByIdInNodeList(nodes[tt], correspIds[i]);
								if (otherEl != null) {
									System.out.println("the corresp id makes sense insofar as it is an id for an alignable element in the other text.");
									// the corresp id makes sense insofar as
									// it is an id for an alignable element in the other text.
									// check further
									otherAEl = AlignmentModel.this.unaligned.get(tt, otherEl);
									if (otherAEl != null) {
										System.out.println("it's an unaligned element all right");
										// it's an unaligned element all right
										link.elementNumbers[tt].add(otherAEl.elementNumber);
										// ###also ...
										//someAligned.add(tt, (Element)otherAEl.element);
										otherAEl.alignmentNumber = link.alignmentNumber;
										someAligned.add(tt, otherAEl);
										//someAligned.print();
									} else {
										System.out.println("error");
										// error
										// ######### trenger feilmelding - ikke kun et pip?
										Toolkit.getDefaultToolkit().beep();
										System.out.println("Hit a case the program can't handle");
										// ####grisete
										stop[0] = true;
										stop[1] = true;
										break;
									}
								}
							}
							if (!(stop[0] && stop[1])) {   // ###
								System.out.println("take one of these elements in the other text and get all the corresponding elements in the first text");
								System.out.println("otherAEl = " + otherAEl);
								// take one of these elements in the other text and get
								// all the corresponding elements in the first text
								String backCorrespValue = otherAEl.element.getAttributes().getNamedItem("corresp").getNodeValue();
								System.out.println("backCorrespValue = " + backCorrespValue);
								String[] backCorrespIds = backCorrespValue.split(" ");
								for (int i = 0; i < backCorrespIds.length; i++) {
									System.out.println("get Node backEl");
									Node backEl = XmlTools.getElementByIdInNodeList(nodes[t], backCorrespIds[i]);
									if ( backEl != null) {
										System.out.println("the ... id makes sense insofar as it is an id for an alignable element in the first text.");
										// the .. id makes sense insofar as
										// it is an id for an alignable element in this text.
										// check further
										AElement backAEl = AlignmentModel.this.unaligned.get(t, backEl);
										if (backAEl != null) {
											System.out.println("it's an unaligned element all right");
											// it's an unaligned element all right
											link.elementNumbers[t].add(backAEl.elementNumber);
											// ###also ...
											//someAligned.add(t, (Element)backAEl.element);
											backAEl.alignmentNumber = link.alignmentNumber;
											someAligned.add(t, backAEl);
											//someAligned.print();
										} else {
											System.out.println("error");
											// error
											// ######### trenger feilmelding - ikke kun et pip?
											Toolkit.getDefaultToolkit().beep();
											System.out.println("Hit a case the program can't handle");
											// ####grisete
											stop[0] = true;
											stop[1] = true;
											break;
										}
									}
								}
							}
							if (!(stop[0] && stop[1])) {   // ###
								// ??? stop text t if no element or no corresp
								// check both sides to see if all the corresp''s agree
								//if link.consistentCorresp() { ##################################i.g.n.m. I.G.N.M.
								// we have made Link out of them.
								// put it in a new someAligned
								someAligned.add(link);
								//someAligned.print();
								//if (someAligned.alignments.size() > 3) {
								//	System.out.println("kill this process"); stop[0] = true; stop[1] = true;
								//}
								if (!someAligned.hasHoles()) {
									// got one or more alignments, with no holes.
									// pop the relevant elements out of unaligned.
									// we don't need their content.
									// we got all the data we need already.
									// just throw them away
									System.out.println("pop and throw them away 2");
									for (t2=0; t2<Alignment.NUM_FILES; t2++) {
										for (int i=0; i<((DefaultListModel)someAligned.elements[t2]).size(); i++) {
											System.out.println("pops from text " + t2);
											AElement aDum = (AElement)(AlignmentModel.this.unaligned.pop(t2));
											String id = aDum.element.getAttributes().getNamedItem("id").getNodeValue();
											System.out.println("popped element with id " + id + " from text " + t2);
											///////////MemTest.print("Heap memory");
											//System.out.println("B");
											//MemTest.print("Tenured Gen", "");
											//System.out.println("unaligned.elements[" + t2+ "].size() = " + unaligned.elements[t2].size());
										}
									}
									//System.out.println("B2");
									//MemTest.print("Tenured Gen", "");
									// process the someAligned and empty it
									System.out.println("process the someAligned and empty it 2");
									toAlign.catch_(someAligned);
									//System.out.println("B3");
									//MemTest.print("Tenured Gen", "");
									someAligned = new AlignmentsEtc();
									//System.out.println("B5");
									//MemTest.print("Tenured Gen", "");
									aligned.pickUp(gui, toAlign.flush(), false);   // false = don't scroll aligned yet (because of a memory leak - ?)
									//System.out.println("B6");
									//MemTest.print("Tenured Gen", "");
									holeyCount = 0;
									//System.out.println("B7");
									//MemTest.print("Tenured Gen", "");
								} else {
									holeyCount++;
									System.out.println("holes 2");
								}
								System.out.println("B8");
								//MemTest.print("Tenured Gen", "");
								//} else {
								//	error
								//}
							}
							System.out.println("C");
							//MemTest.print("Tenured Gen", "");
						}
						System.out.println("D");
						//MemTest.print("Tenured Gen", "");
					}
					System.out.println("E");
					//MemTest.print("Tenured Gen", "");
					
				}
				System.out.println("F");
				//MemTest.print("Tenured Gen", "");
				
				if (holeyCount > 10) {
					// we have done ... alignments, and someAligned is still holey.
					// we suspect something is wrong
					System.out.println("kill this process"); stop[0] = true; stop[1] = true;
				}
				
				System.out.println("før while. stop[0]=" + stop[0] +", stop[1]=" + stop[1]);
				
			} while(!(stop[0] && stop[1]));
			System.out.println("G");
			//MemTest.print("Tenured Gen", "");
			
			if (!someAligned.empty()) {
				// error
				// ######### trenger feilmelding - ikke kun et pip?
				//dette blir ikke bra hvis someAligned har hull!!! ########################
				Toolkit.getDefaultToolkit().beep();
				System.out.println("!someAligned.empty()");
				System.out.println("Dodgy case??????????????");
				toAlign.catch_(someAligned);
				someAligned = new AlignmentsEtc();
			}
			
		}
		
		// scroll aligned. (waited until now because of a memory leak - ?)
		for (int t=0; t<Alignment.NUM_FILES; t++) {
			gui.alignedListBox[t].ensureIndexIsVisible(aligned.elements[t].getSize()-1);
		}
		
	}
	
	// 2006-08-15
	
	void break_(AlignGui gui) {
		Toolkit.getDefaultToolkit().beep();
	}
	// end 2006-08-15
	
	
	
	
	
	/*
	 // the elements we pick from unaligned
	  // will refer to elements in the other text(s).
	   // we put these other elements (or to be precise, their id''s)
	    // on a wanted list (or to be precise, one list for each text).
	     // at the same time we strike found elements from the wanted list.
	      // when the wanted list is empty we have picked
	       // the elements for _one_ complete alignment,
	        // or _several_ alignments in the case of crossed relations,
	         // or _no_ alignments in the case we run out of elements with corresp attributes,
	          // or run out of text altogether
	           
	           // initialize wanted list.
	            // and initialize pointers to next available elements in unaligned
	             // we don''t pick elements as in actually removing the elements from unaligned -
	              // not until we have found complete alignments.
	               // we just have pointers (indexes) to keep track of how far we have come.
	                // the pointers are kept in array ''next''
	                 
	                 Wanted wanted = new Wanted();   // array with one list of id''s per text
	                 Picked picked = new Picked();   // array with one (or no) id per text
	                 int[] next = new int(Alignment.NUM_FILES);
	                 for (int t=0; t<Alignment.NUM_FILES; t++) {
	                 next[t] = 0;
	                 }
	                 
	                 // ...
	                  
	                  boolean complete = false;
	                  do {
	                  
	                  int countPicked = 0;   // counts the elements picked in ####this round
	                  for (int t=0; t<Alignment.NUM_FILES; t++) {
	                  
	                  // ####### number of the other text
	                   if (t == 0) {
	                   tt = 1;
	                   } else {
	                   tt = 0;
	                   }
	                   
	                   while (wanted.wants(t)) {   // for each text t the first call to method wants() is a special case which returns true
	                   
	                   // there are elements from text t on the wanted list.
	                    // pick next element from text t
	                     
	                     if (next[t] <= unaligned.elements[t].size() - 1) {
	                     
	                     AElement aElement = (AElement)unaligned.elements[t].get(next[t]);
	                     Element node = aElement.element;
	                     String id = node.getAttribute("id");
	                     picked.add(t, id);
	                     
	                     // get the references this element has to elements in other text,
	                      // and put the other elements on the wanted list
	                       
	                       String[] refs = node.getAttribute("corresp").split(" ");
	                       for (int i=0; i<refs.length; i++) {
	                       String ref = refs[i];
	                       // is this ref a reference to a "relevant" element, e.g, an <s>?
	                        // if not, it must be the element in a 1-0 or 0-1 alignment,
	                         // with a reference to an ancestor element, e.g, a <p>
	                          if (XmlTools.getElementByIdInNodeList(nodes[tt], ref) {
	                          // "relevant"
	                           ...
	                           wanted.add(tt, ref);
	                           } else {
	                           // ancestor
	                            ...make sure to keep the exact id?...
	                            }
	                            }
	                            
	                            // remove the picked ones from the wanted list
	                             
	                             wanted.remove(picked);
	                             
	                             // update pointer to next element to pick
	                              
	                              next[t]++;
	                              
	                              } else {
	                              
	                              // error. no more elements in this text
	                               
	                               ...
	                               
	                               }
	                               
	                               }
	                               
	                               }
	                               
	                               // ...
	                                ...
	                                
	                                } while (!(wanted.empty() || picked.empty()));
	                                
	                                if (...) {
	                                // found a set of corresponding elements.
	                                 // ### or perhaps several ones
	                                  // make an alignment out of them?
	                                   // or 'more' them up from unaligned to to-align?
	                                    // ### and do something extra if there are crossed relations?
	                                     ...
	                                     if (...) {
	                                     // something wrong with corresp attrs
	                                      ...
	                                      done = true;
	                                      // ### or throw exception?
	                                       }
	                                       // then 'align' them
	                                        ...
	                                        } else {
	                                        done = true;
	                                        }
	                                        
	                                        } while(...);
	                                        
	                                        }
	                                        
	                                        }
	                                        
	                                        
	                                        
	                                        
	                                        getIndexOfElementByIdInNodeList(nodes[...], id);
	                                        
	                                        class Link {
	                                        int alignmentNumber;
	                                        Set[] elementNumbers;
	                                        
	                                        */
	
	
	
	
	
	
	
	
	
	
	
	
	void less(AlignGui gui, int t) {   // package access
		
		////System.out.println("at model.less()");
		//unaligned.catch_(t, toAlign.drop(t));
		unaligned.catch_(t, toAlign.drop(gui, t));   // ### 2006-03-30
		computeMatches(gui);   // ### compute and display
		//ShowCompare.clear(gui);
		gui.compareInfoPanel.off();
		gui.compareInfoPanel.repaint();
		
		// 2006-10-03
		// update aligned/total ratio in status line
		gui.model.setMemoryUsage(gui);   // 2006-10-03
		gui.model.updateAlignedTotalRatio(gui);
		
	}
	
	void more(AlignGui gui, int t) {   // package access
		
		//System.out.println("model sin more(). gui = " + gui);
		//System.out.println("\nmodel sin more(). t = " + t);
		//////////MemTest.print("Heap memory", "");
		//MemTest.print("Tenured Gen", "");
		//toAlign.pickUp(gui, t, unaligned.pop(t));
		toAlign.pickUp(t, unaligned.pop(t));
		computeMatches(gui);   // ### compute and display);
		//ShowCompare.clear(gui);
		gui.compareInfoPanel.off();
		gui.compareInfoPanel.repaint();
		
		// 2006-10-03
		// update aligned/total ratio in status line
		gui.model.setMemoryUsage(gui);   // 2006-10-03
		gui.model.updateAlignedTotalRatio(gui);
		
	}
	
}



/*
 
 
 
 gui.statusLine.setText("");
 int percentDone = 0;
 gui.statusLine.setProgress(percentDone);
 gui.statusLine.repaint_();
 int numElements = nodes[t].getLength();
 for (int i=0; i<numElements; ++i) {
 AElement element = new AElement(nodes[t].item(i), i);
 unaligned.add(t, element);
 percentDone = Math.round((float)((float)i / numElements * 100.0));
 ////gui.counterDoc.insertString(0, Integer.toString(i+1), null);   // ¤¤¤ problem. blir ikke frisket opp
  //gui.counter.setText(Integer.toString(i+1));
   gui.statusLine.setText(Integer.toString(i+1));
   ////Thread.sleep(100);
    gui.statusLine.setProgress(percentDone);
    gui.statusLine.repaint_();
    }
    ////gui.counterDoc.insertString(0, "", null);
     //gui.counter.setText("");
      gui.statusLine.setText("");
      gui.statusLine.repaint_();
      
      //return true;
       
       
       
       */
/*
 for (int t=0; t<Alignment.NUM_FILES; t++) {
 elementsInfo[t].setFirst(model, ix[t], t);
 */

/*
 for (int t=0; t<Alignment.NUM_FILES; t++) {
 if (position[t] < elementsInfo[t].getFirst(model, t)) {   skal vel være .getFirst()
 // outside (before) matrix.
  // reason: path is e.g 0-1 step
   return Integer.MIN_VALUE;   (float)(Integer.MIN_VALUE);   // #####
   }
   }
   */
