	int clusterScoreMethod;
	Clusters(int clusterScoreMethod) {
		clusters = new ArrayList();
		this.clusterScoreMethod = clusterScoreMethod;
	}







	// a cluster contains references to words in several text (in practice 2).
	// some texts may have more references than others,
	// and this method finds the largest number of references
	int getScore() {
		if (clusterScoreMethod == 1) {
			// each cluster scores exactly 1
			return 1;
		}
		int high = 0;
		int low = Integer.MAX_VALUE;
		Iterator rIt;
		Ref ref;
		for (int t=0; t<Alignment.NUM_FILES; t++) {
			int count = 0;
			rIt = refs.iterator();
			while (rIt.hasNext()) {
				ref = (Ref)rIt.next();
				if (ref.isInText(t)) {
					count++;
				}
			}
			if (clusterScoreMethod == 2) {
				// each cluster scores equal to its minimal dimension.
				// e.g if each of 2 words in one text match each of 3 words in the other text,
				//   X X X
				//   | | |
				// X-+-+-+
				//   | | |
				// X-+-+-+
				// the score is min(2, 3) = 2.
				// some clusters may not be regular grids, e.g
				//   X X X X
				//   | | | |
				// X-+-+ | |
				//   | | | |
				// X-+-+-+ |
				//       | |
				// X-----+-+
				// in the latter example the minimal dimension is 3
				low = Math.min(low, count);
			} else { // if (clusterScoreMethod == 3)
				// each cluster scores equal to its maximal dimension.
				high = Math.max(high, count);
			}
		}
		if (clusterScoreMethod == 2) {
			return low;
		} else { // if (clusterScoreMethod == 3)
			return high;
		}
	}

	String getWords() {
		Cluster sortedCluster = (Cluster)this.clone();
		Collections.sort(sortedCluster.refs, new RefComparator());
		Iterator rIt = sortedCluster.refs.iterator();
		String retVal = "";
		int prevT = -1;
		int numSlashes;
		boolean first = true;
		boolean firstInCurrText;
		while (rIt.hasNext()) {
			Ref ref = (Ref)rIt.next();
			firstInCurrText = false;
			if (ref.t > prevT) {
				if (first) {
					numSlashes = ref.t - prevT -1;
					first = false;
				} else {
					numSlashes = ref.t - prevT;
				}
				for (int i=0; i<numSlashes; i++) {
					retVal += "/";
				}
				firstInCurrText = true;
			}
			if (firstInCurrText) {
				firstInCurrText = false;
			} else {
				retVal += ",";
			}
			retVal += ref.word;
			prevT = ref.t;
		}
		return retVal;
	}






















==================
har tatt denne over til aktuelle versjon
==================

/*
 * Clusters.java
 *
 * ... for dice ...
 * ...
 * @author Oystein Reigem
 */

package aksis.alignment;

import java.util.*;

/**
 * single word reference in a Cluster in Clusters.
 */
class Ref {

	// a reference to word x in the considered elements of text t

	// type of match.
	// if the value is non-negative the match is an anchor word match,
	// and the value is the number of the matching entry in the anchor word list.
	// if the value is negative the match is some other match -
	// proper name match, Dice match, etc, depending on the value
	Match.PROPER, Match.DICE, Match.NUMBER £££££££££
	private int matchType;   // 2006-04-05
	// weight
	private float weight;   // 2006-04-05
	// text number
	private int t;
	// word number (position) within the relevant elements of that text
	private int pos;
	// word as written in the text.
	// can be a phrase, in which case the word number x
	// is the position of the first word of the phrase ££££££££££££
	private String word;

	Ref(int matchType, float weight, int t, int x, String word) {   // 2006-04-05
		this.matchType = matchType;   // 2006-04-05
		this.weight = weight;   // 2006-04-05
		this.t = t;
		this.pos = pos;
		this.word = word;
	}

	// checks if equal content

	boolean matches(Ref otherRef) {
		// (not necessary to check match type or word)
		return ((this.t == otherRef.t) && (this.pos == otherRef.pos));
	}

	boolean isInText(int t) {
		return (this.t == t);
	}

	// for debugging purposes

	public String toString() {
		return "[" + "matchType=" + matchType + ";" + "weight=" + weight + ";" + "t=" + t + ";" + "pos=" + pos + ";" + "word=" + word + "]";   // 2006-04-05
	}

}

/**
 * single cluster in Clusters.
 * list of all word references belonging to the cluster
 */
class Cluster implements Cloneable {

	private List refs;   // list of Ref

	Cluster() {
		refs = new ArrayList();
	}

    public Object clone() {
        try {
            return super.clone();
        } catch (CloneNotSupportedException e) {
            throw new Error("This error should not occur since we implement Cloneable");
        }
    }

	List getRefs() {
		return refs;
	}


	void add(Ref otherRef) {

		Iterator rIt = refs.iterator();
		Ref ref;
		while (rIt.hasNext()) {
			ref = (Ref)rIt.next();
			if (ref.matches(otherRef)) {
				// the cluster already contains this reference
				// (necessarily with the same word)
				return;
			}
		}
		// this reference is new.
		// add it to the cluster
		refs.add(otherRef);

		return;
	}

	boolean matches(Ref otherRef) {
		// does this cluster match other ref?
		//
		Iterator rIt = refs.iterator();
		Ref ref;
		while (rIt.hasNext()) {
			ref = (Ref)rIt.next();
			if (ref.matches(otherRef)) {
				return true;
			}
		}
		return false;
	}

	boolean matches(Cluster otherCluster) {
		Iterator orIt = otherCluster.refs.iterator();
		Ref otherRef;
		while (orIt.hasNext()) {
			otherRef = (Ref)orIt.next();
			if (this.matches(otherRef)) {
				return true;
			}
		}
		return false;
	}

	void add(Cluster otherCluster) {

		Iterator orIt = otherCluster.refs.iterator();
		Ref otherRef;
		while (orIt.hasNext()) {
			otherRef = (Ref)orIt.next();
			this.add(otherRef);
		}

	}

	// a cluster contains references to words in several text (in practice 2).
	// some texts may have more references than others,
	// and this method finds the largest number of references
	int getScore(int clusterScoreMethod) {   // 2006-04-05
		if (clusterScoreMethod == 1) {
			// each cluster scores exactly 1
			return 1;
		} else {
			int high = 0;
			int low = Integer.MAX_VALUE;
			Iterator rIt;
			Ref ref;
			for (int t=0; t<Alignment.NUM_FILES; t++) {
				int count = 0;
				rIt = refs.iterator();
				while (rIt.hasNext()) {
					ref = (Ref)rIt.next();
					if (ref.isInText(t)) {
						count++;
					}
				}
				if (clusterScoreMethod == 2) {
					// each cluster scores equal to its minimal dimension.
					// e.g if each of 2 words in one text match each of 3 words in the other text,
					//   X X X
					//   | | |
					// X-+-+-+
					//   | | |
					// X-+-+-+
					// the score is min(2, 3) = 2.
					// some clusters may not be regular grids, e.g
					//   X X X X
					//   | | | |
					// X-+-+ | |
					//   | | | |
					// X-+-+-+ |
					//       | |
					// X-----+-+
					// in the latter example the minimal dimension is 3
					low = Math.min(low, count);
				} else { // if (clusterScoreMethod == 3)
					// each cluster scores equal to its maximal dimension.
					high = Math.max(high, count);
				}
			}
			if (clusterScoreMethod == 2) {
				return low;
			} else { // if (clusterScoreMethod == 3)
				return high;
			}
		}
	}

	String getWords() {
		Cluster sortedCluster = (Cluster)this.clone();
		Collections.sort(sortedCluster.refs, new RefComparator());
		Iterator rIt = sortedCluster.refs.iterator();
		String retVal = "";
		int prevT = -1;
		int numSlashes;
		boolean first = true;
		boolean firstInCurrText;
		while (rIt.hasNext()) {
			Ref ref = (Ref)rIt.next();
			firstInCurrText = false;
			if (ref.t > prevT) {
				if (first) {
					numSlashes = ref.t - prevT -1;
					first = false;
				} else {
					numSlashes = ref.t - prevT;
				}
				for (int i=0; i<numSlashes; i++) {
					retVal += "/";
				}
				firstInCurrText = true;
			}
			if (firstInCurrText) {
				firstInCurrText = false;
			} else {
				retVal += ",";
			}
			retVal += ref.word;
			prevT = ref.t;
		}
		return retVal;
	}

	// for debugging purposes
	public String toString() {
		Iterator rIt = refs.iterator();
		Ref ref;
		String retVal = "";
		boolean first = true;
		retVal += "(";
		while (rIt.hasNext()) {
			ref = (Ref)rIt.next();
			if (first) {
				first = false;
			} else {
				retVal += ",";
			}
			retVal += ref.toString();
		}
		retVal += ")";
		return retVal;
	}

}

/**
 * information about how related words in the texts collect in clusters
 */
class Clusters {

	private List clusters;   // list of Cluster

	Clusters() {
		clusters = new ArrayList();
	}

	// word x in text t (word1) is related to word y in text tt (word2).
	// matchType is the type of match, as explained elsewhere.
	// weight is the weight assigned to the match.
	// add that information

	void add(int matchType, float weight, int t, int tt, int x, int y, String word1, String word2) {


		// add new word references

		add(new Ref(matchType, weight, t,  x, word1));
		add(new Ref(matchType, weight, tt, y, word2));

	}

	void add(Ref ref) {

		// make a new cluster from the new word reference

		Cluster newCluster = new Cluster();
		newCluster.add(ref);

		// check the new cluster against all existing clusters to see if there is overlap

		List overlaps = new ArrayList();   // list of Cluster

		Iterator cIt = clusters.iterator();
		Cluster cluster;
		while (cIt.hasNext()) {
			cluster = (Cluster)cIt.next();
			if (newCluster.matches(cluster)) {
				overlaps.add(cluster);
			}
		}

		// merge the new cluster with all overlapping clusters

		Cluster mergedCluster = newCluster;
		Iterator oIt = overlaps.iterator();
		while (oIt.hasNext()) {
			cluster = (Cluster)oIt.next();
			mergedCluster.add(cluster);
			clusters.remove(cluster);
		}

		// add the new, merged cluster
		clusters.add(mergedCluster);



	}

	// merge two Clusters

	void add(Clusters otherClusters) {
		Iterator ocIt = cs.otherClusters.iterator();
		while (ocIt.hasNext()) {
			otherCluster = (Cluster)ocIt.next();
			add(otherCluster);
		}
	}

	// add a Cluster to a Clusters

	void add(Cluster otherCluster) {
		Iterator orIt = otherCluster.getRefs().iterator();
		while (orIt.hasNext()) {
			otherRef = (Ref)orIt.next();
			add(otherRef);
		}
	}

	int getScore() {


		int score = 0;

		// each cluster adds to the score.
		// each cluster contains references to words in several text (in practice 2).
		// the score is the largest number of referred words in any text

		Iterator cIt = clusters.iterator();
		Cluster cluster;
		while (cIt.hasNext()) {

			cluster = (Cluster)cIt.next();

			score += cluster.getScore();

		}

		return score;

	}


	List getDetails(int indentLevel) {

		Iterator cIt = clusters.iterator();
		Cluster cluster;

		List retVal = new ArrayList();

		while (cIt.hasNext()) {






			cluster = (Cluster)cIt.next();



			String indentation = "";
			for (int i = 0; i < indentLevel; i++) { indentation += ElementInfoToBeCompared.INDENT; }   // €€€€€€€€€€ ElementInfoToBeCompared €€€ INDENT
			retVal.add(indentation + cluster.getWords());

		}

		return retVal;

	}

	// for debugging purposes
	public String toString() {
		Iterator cIt = clusters.iterator();
		Cluster cluster;
		String retVal = "";
		boolean first = true;
		retVal += "{ ";
		while (cIt.hasNext()) {
			cluster = (Cluster)cIt.next();
			if (first) {
				first = false;
			} else {
				retVal += " ; ";
			}
			retVal += cluster.toString();
		}
		retVal += " }";
		return retVal;
	}

}





======================================
har tatt denne over til aktuelle versjon
==================

	public List toList() {


		if (score <= -0.9) {   // i.e, == -1.0, i.e, not calculated
			// not been calculated yet.


			score = (float)0.0;


			if (empty()) {

				// keep score 0. keep str ""


			} else {

				int t;
				int tt;
				Iterator it;
				Iterator it1;
				Iterator it2;


				// 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 where to insert the score later
				int wordMethodsScoreLineNumber = ret.size() - 1;

				//////////////////
				// anchor words //
				//////////////////

				Clusters anchorWordClusters = new Clusters();

				int anchorWordScore = 0;

				AnchorWordHit hit;
				int index;
				int count;
				int smallest;
				int smallestCount;
				boolean presentInAllTexts;


				// for each text t make a list hits[t] of anchor word hits
				// for (from) the elements under consideration from that text.
				// 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[] hits = new ArrayList[Alignment.NUM_FILES];
				for (t=0; t<Alignment.NUM_FILES; t++) {
					hits[t] = new ArrayList();

					it = info[t].iterator();

					while (it.hasNext()) {
						ElementInfo info1 = (ElementInfo)it.next();
						it2 = info1.anchorWordHits.hits.iterator();
						while (it2.hasNext()) {
							hit = (AnchorWordHit)it2.next();

							hits[t].add(hit);
						}
					}

				}

				// 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());

				}

				// 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;

				}

				// 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) {

					// find smallest anchor word list index in remaining hits.
					// check if it is present in all texts
					smallest = Integer.MAX_VALUE;
					smallestCount = 0;

					for (t=0; t<Alignment.NUM_FILES; t++) {

						if (current[t] < hits[t].size()) {

							// 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;

							} else if (hit.getIndex().intValue() == smallest) {
								// same smallest. increment count
								smallestCount++;
							} // else not a smallest
						}
					}
					presentInAllTexts = (smallestCount == Alignment.NUM_FILES);
					// in the following: collect data for output only if the hit### was present in all texts
					// ...
					if (smallest == Integer.MAX_VALUE) {

						// 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.





						for (t=0; t<Alignment.NUM_FILES; t++) {

							count = 0;

							boolean first = true;
							if (current[t] < hits[t].size()) {

								// 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++) {

									hit = (AnchorWordHit)hits[t].get(c);

									index = hit.getIndex().intValue();
									if (index == smallest) {


										// add to cluster list
										pos = hit.getPos();
										word = hit.getWord();
										matchType = Match.ANCHOR;   £££
										weight = 1.0f;   £££
										anchorWordClusters.add(new Ref(matchType, weight, t, pos, word));   // ### heller en addRef-metode? handler om grenseoppgang mellom clustergreiene og utsiden
										// ...
										count++;

									} else {
										done2 = true;
									}
									if (c+1 >= hits[t].size()) {
										done2 = true;
									}
								}
								// ...
								current[t] += count;

							}
						}
					}
				}

				// ...

				anchorWordScore = anchorWordClusters.getScore();

				// do reporting###

				// next line of info...
				//if (anchorWordScore > 0) {   // ### ryddigere med samme syntaks alltid
					retLine = INDENT + INDENT + "Anchor word score: " + anchorWordScore;   // 2006-04-05
				//} else {
				//	retLine = INDENT + "No anchor word matches. Score: 0";
				//}
				ret.add(retLine);

				indentLevel = 3;
				ret.addAll(anchorWordClusters.getDetails(indentLevel));   // getDetails() does its own indentation and endline. ### ikke helt bra?

				//////////////////
				// proper names //
				//     and      //
				//     dice     //
				//////////////////

				// 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.
				// (usually all the words in a cluster will be related to each other,
				// but not necessarily.)
				String word1;
				String word2;
				Clusters properNameClusters = new Clusters();
				Clusters diceClusters = new Clusters();
				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 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];

								// ... 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];

										// 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

											matchType = Match.PROPER;
											weight = 1.0f;
											properNameClusters.add(matchType, weight, t, tt, x, y, word1, word2);

										}

										// 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()) {

												// the words are related.
												// add to cluster list

												matchType = Match.DICE;   £££
												weight = 1.0f;   £££
												diceClusters.add(matchType, weight, t, tt, x, y, word1, word2);

											}

										}

									}
								}

							}

						}
					}
				}

				properNameScore = properNameClusters.getScore();

				diceScore = diceClusters.getScore();

				// ...


				retLine = INDENT + INDENT + "Proper name score: " + properNameScore;
				ret.add(retLine);

				indentLevel = 3;
				ret.addAll(properNameClusters.getDetails(indentLevel));   // getDetails() does its own indentation and endline. ### ikke helt bra?

				retLine = INDENT + INDENT + "Dice score: " + diceScore;
				ret.add(retLine);

				indentLevel = 3;
				ret.addAll(diceClusters.getDetails(indentLevel));   // getDetails() does its own indentation and endline. ### ikke helt bra?

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

				// common score for anchor words, proper names and dice

				Clusters commonClusters = new Clusters();
				commonClusters.add(anchorWordClusters);
				commonClusters.add(properNameClusters);
				commonClusters.add(diceClusters);

				commonScore = commonClusters.getScore();

				// go back and insert the common score for the word based methods
				ret[wordMethodsScoreLineNumber] = ret.get(wordMethodsScoreLineNumber) + commonScore;

				score += commonScore;

				////////////////////////////////
				// scoring special characters //
				////////////////////////////////

				int scoringCharacterScore = 0;

				// check all the ... ... ...

				String char1;
				String char2;
				Clusters scoringCharacterClusters = new Clusters();
				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();

							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();

									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

											matchType = Match.SCORING_CHARACTERS;   £££ irrelevant
											weight = 1.0f;   £££
											scoringCharacterClusters.add(matchType, weight, t, tt, x, y, char1, char2);

										}

									}
								}

							}

						}
					}
				}

				scoringCharacterScore += scoringCharacterClusters.getScore();

				// ...


				retLine = INDENT + "Special characters score: " + scoringCharacterScore;
				ret.add(retLine);

				score += scoringCharacterScore;

				indentLevel = 2;
				ret.addAll(scoringCharacterClusters.getDetails(indentLevel));   // getDetails() does its own indentation and endline. ### ikke helt bra?

				////////////
				// length //
				////////////

				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], elementCount[0], elementCount[1], model.getLengthRatio());

				retLine = "Lengths " + length[0] + " (" + length[0]*model.getLengthRatio() + ") and " + length[1];
				if (score > scoreBefore) {
					retLine += " match well,";
					ret.add(retLine);
					retLine = INDENT + "increasing score from " + scoreBefore + " to " + score;
					ret.add(retLine);
				} else if (score < scoreBefore) {
					retLine += " don't match well,";
					ret.add(retLine);
					retLine = INDENT + "reducing score from " + scoreBefore + " to " + score;
					ret.add(retLine);
				} else {
					retLine += " match so-so,";
					ret.add(retLine);
					retLine = INDENT + "making no change to the score " + 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;
				}

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

				retLine = "Total match score: " + score;
				// main header. insert at top
				ret.add(0, retLine);

			}

		}

		// return textual version as a List
		return ret;

	}



======================================
har tatt denne over til aktuelle versjon
==================

/*
 * AnchorWordHits.java
 *
 * ...
 * ...
 * ...
 */

package aksis.alignment;

// €€€ ikke sjekket nøyaktig hva vi trenger
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;
import java.lang.String;
import java.io.*;
//import aksis.awt2.AwtUtil;

// ...

class AnchorWordHit {

	Integer index;   // refers to entry in anchor word list
	int pos;   // position of word within its element. 2006-04-04
	String word;   // word as written in the text

	public AnchorWordHit(Integer index, int pos, String word) {   // 2006-04-04
		this.index = index;
		this.pos = pos;   // 2006-04-04
		this.word = word;
	}

	public Integer getIndex() {
		return index;
	}

	public String getWord() {
		return word;
	}

	// 2006-04-04

	public int getPos() {
		return pos;
	}

	// ### for debugging
	public String toString() {
		return "(index=" + index.intValue() + ";pos=" + pos ";word=" + word + ")";
	}

}

class AnchorWordHits {

	java.util.List hits;

	public AnchorWordHits () {
		hits = new ArrayList();
	}

	public void add(AnchorWordHit hit) {
		hits.add(hit);
	}

	// ### for debugging
	public String toString() {
		Iterator it = hits.iterator();
		StringBuffer ret = new StringBuffer();
		ret.append("[");
		while (it.hasNext()) {
			if (ret.equals("[")) { ret.append(","); }
			ret.append((AnchorWordHit)it.next());
		}
		return new String(ret);
	}

}

==================
gjorde ingen endringer i denne?
==================

/*
 * AnchorWordList.java
 *
 * ...
 * ...
 * ...
 */

package aksis.alignment;

// €€€ ikke sjekket nøyaktig hva vi trenger
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;
import java.lang.String;
import java.io.*;
//import aksis.awt2.AwtUtil;

/**
 * entry in anchor word list.
 * array with one element for each language.
 * each language element is a list of arrays.
 * each array contains a phrase with one word per element.
 * most phrases will have just one word
 */
class AnchorWordListEntry {

	// for hvert språk en liste av "synonymer"
	java.util.List[] language = new ArrayList[Alignment.NUM_FILES];   // €€€ skal vel heller være 2

	public AnchorWordListEntry(String anchorWordListEntryText) throws Exception {   // anchorWordListEntryText syntax must be e.g "en,et/one" with delimiters "/" and "'" and no whitespace whatsoever

		//System.out.println("anchorWordListEntryText.length()=" + anchorWordListEntryText.length());
		//System.out.println("anchorWordListEntryText=" + anchorWordListEntryText);
		String[] temp = new String[Alignment.NUM_FILES];

		if (anchorWordListEntryText.length() > 0) {

			//System.out.println("splitter entry");
			String[] data = anchorWordListEntryText.split("/");   // split entry into array of data for each text/language
			//System.out.println("har splittet entry");

			if (data.length < Alignment.NUM_FILES) {
				throw new Exception("No slash: " + anchorWordListEntryText);   // §§§
			} else if (data.length > Alignment.NUM_FILES) {
				throw new Exception("Too many slashes: " + anchorWordListEntryText);   // §§§
			}

			for (int t=0; t<Alignment.NUM_FILES; t++) {
				language[t] = new ArrayList();
				//System.out.println("splitter data");
				String[] syns = data[t].split(",");   // split data for one language into array of "synonyms". each synonym can be a phrase
				//System.out.println("har splittet data");
				for (int ph=0; ph<syns.length; ph++) {
					//System.out.println("splitter frase");
					String[] words = syns[ph].split(" ");  // split phrase into array of words. in practice most phrases will contain just one word
					//System.out.println("har splittet frase");
					java.util.List phrase = new ArrayList();   // array to contain one phrase, with one word per element
					for (int w=0; w<words.length; w++) {
						//System.out.println("tar ut ord nr " + w);
						String word = words[w].trim();
						//System.out.println("har tatt ut ord nr " + w + ". ord = " + word);
						if (!(word.equals(""))) {
							//System.out.println("legger til ord i frase-liste");
							phrase.add(word);
							//System.out.println("har lagt til ord i frase-liste");
						}
					}
					if (phrase.size() > 0) {
						//System.out.println("legger til frase i data-liste");
						language[t].add(phrase);
						//System.out.println("har lagt til frase i data-liste");
					}
					//System.out.println("ferdig med en frase");
				}
				//System.out.println("ferdig med data for et språk");
			}
			//System.out.println("ferdig med hele entry");

		}

	}

}

/**
 * anchor word list.
 * list, with each element a AnchorWordListEntry.
 */
class AnchorWordList {

	java.util.List entries = new ArrayList();   // explicit to avoid ambiguousness

	AlignmentModel model;

	//AnchorWordList() {
	//	// €€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€ foreløpig hardkodet €
	//	entries.add(new AnchorWordListEntry("paragraph/avsnittet"));
	//	entries.add(new AnchorWordListEntry("inflation/inflasjon,inflasjonen"));
	//	entries.add(new AnchorWordListEntry("policy/pengepolitikken"));
	//	entries.add(new AnchorWordListEntry("krone/krone,kronen,kronens,kroner"));
	//	entries.add(new AnchorWordListEntry("euro/euro"));
	//}

	AnchorWordList(AlignmentModel model) {
		this.model = model;
	}

	public void load(ArrayList lines) {

		//System.out.println("AnchorWordList sin load()");
		//System.out.println("lines.size() = " + lines.size());
		// clear list
		//java.util.List entries = new ArrayList(); denne førte til at model sin anchorWordList likevel ikke ble satt. har ikke tenkt gjennom hvorfor
		entries.clear();

		// load list
		boolean ok = true;
		Iterator it = lines.iterator();
		while (it.hasNext()) {
			String line = ((String)(it.next())).trim();
			if (line.length() > 0) {
				//System.out.println("line='"+line+"'");
				try {
					entries.add(new AnchorWordListEntry(line));
				} catch (Exception e) {
					System.err.println("Error in anchor word entry: " + e.getMessage());
					JOptionPane.showMessageDialog(
						null,
						"Error in anchor word entry: " + e.getMessage(),
						"Error in anchor word entry",
						JOptionPane.ERROR_MESSAGE
					);
					ok = false;
					break;
				}
			}
			//System.out.println("entries.size() = " + entries.size());
		}

		if (!ok) {
			// error occurred. clear list again
			System.out.println("Error occurred. clear list again");
			//entries = new ArrayList();
			entries.clear();   // €€€ er ikke dette bedre?
		}

	}

	public void display(JTextArea content) {

		if (entries != null) {

			Iterator eIt = entries.iterator();
			while (eIt.hasNext()) {

				//System.out.println("neste entry");

				StringBuffer anchorWordListEntryText = new StringBuffer("");
				AnchorWordListEntry anchorWordListEntry = (AnchorWordListEntry)eIt.next();
				for (int t=0; t<Alignment.NUM_FILES; t++) {
					//System.out.println("t=" + t);
					if (t > 0) {
						//System.out.println("append /");
						anchorWordListEntryText.append("/");   // ### sett konstanter et felles sted
					}
					java.util.List synonyms = anchorWordListEntry.language[t];
					Iterator sIt = synonyms.iterator();
					boolean first = true;
					//System.out.println("antall fraser=" + synonyms.size());
					while (sIt.hasNext()) {
						//System.out.println("neste frase");
						if (first) {
							first = false;
						} else {
							//System.out.println("append ,");
							anchorWordListEntryText.append(",");   // ### sett konstanter et felles sted
						}
						//word = (String)wIt.next();
						//anchorWordListEntryText.append(word);
						java.util.List phrase = (java.util.List)sIt.next();
						Iterator wIt = phrase.iterator();
						boolean firstW = true;
						//System.out.println("antall ord=" + phrase.size());
						while (wIt.hasNext()) {
							//System.out.println("neste ord");
							if (firstW) {
								firstW = false;
							} else {
								//System.out.println("append space");
								anchorWordListEntryText.append(" ");   // ### sett konstanter et felles sted
							}
							String word = (String)wIt.next();
							//System.out.println("append word");
							anchorWordListEntryText.append(word);
						}
					}
				}

				content.append(anchorWordListEntryText + "\n");

			}

		}
	}

	public AnchorWordListEntry getEntry(int i) {

		return (AnchorWordListEntry)entries.get(i);

	}

	/**
	 * takes as the first argument an array of words.
	 * these are all the words from one alignable element, in original order.
	 * searches anchor word list for matches with these words.
	 * the t-th side of the anchor word list is searched (0=left, 1=right).
	 * returns hits (matches) as list of numbers.
	 * each number is the number of an item (i.e, line???) in the anchor word list.
	 */
	public AnchorWordHits getAnchorWordHits(String[] words, int t) {
		//System.out.println(">>>>>>>>>>>getAnchorWordHits<<<<<<<<<<<<");
		AnchorWordHits ret = new AnchorWordHits();
		//
		Iterator aIt = this.entries.iterator();
		int anchorWordEntryCount = 0;
		while (aIt.hasNext()) {
			//System.out.println("anchorWordEntryCount = " + anchorWordEntryCount);
			//java.util.List synonyms = ((AnchorWordListEntry)(aIt.next())).language[t];
			AnchorWordListEntry entry = (AnchorWordListEntry)(aIt.next());
			java.util.List synonyms = (java.util.List)(entry.language[t]);
			Iterator sIt = synonyms.iterator();
			while (sIt.hasNext()) {
				java.util.List anchorPhrase = (java.util.List)sIt.next();
				//System.out.println("anchorPhrase = " + anchorPhrase);
				for (int w=0; w<words.length - anchorPhrase.size() + 1; w++) {
					boolean success = true;
					// loop over word in phrase
					StringBuffer matchingPhrase = new StringBuffer();   // the actual phrase occurring in the text
					for (int w2=0; w2<anchorPhrase.size(); w2++) {
						String word = words[w+w2];
						String anchorWord = (String)(anchorPhrase.get(w2));
						//System.out.println("word = " + word);
						//if (anchorWord == word) {
						//if (anchorWord.equals(word)) {
						//if (anchorWord.equalsIgnoreCase(word)) {   // ### eller skal det være noe mer sofistikert? alfa = Alfa, men ikke ALFA? alfa = Alfa, men kun hvis Alfa er første ord i setningen?
						if (SimilarityUtils.anchorMatch(anchorWord, word)) {
							if (w2 > 0) matchingPhrase.append(" ");
							matchingPhrase.append(word);
						} else {
							success = false;
							break;
						}
					}
					if (success) {
						//System.out.println("match!");
						//System.out.println("match. matchingPhrase = " + matchingPhrase);
						AnchorWordHit hit = new AnchorWordHit(new Integer(anchorWordEntryCount), ..., new String(matchingPhrase));   // 2006-04-04
						ret.add(hit);
					}
				}
			}
			anchorWordEntryCount++;
		}
		return ret;
	}

	// €€€€€€€€€€€€€€€€€€€€€€ hører vel ikke hjemme her, men hvor skal jeg ha den?

	/**
	 * takes array.
	 * extracts words starting with uppercase letter.
	 * returns as list.
	 */
	public java.util.List getProperNames(String[] words) {
		//System.out.println("getProperNames. words.length=" + words.length);
		java.util.List ret = new ArrayList();
		for (int w=0; w<words.length; w++) {
			//System.out.println("words["+w+"]=" + words[w]);
			String word = words[w];
			if (word.length() > 0) {
				if (Character.isUpperCase(word.charAt(0))) {   // Character.isUppercase() ? cast med (char), nei (Character) ?
					ret.add(word);
				}
			}
		}
		return ret;
	}

	/**
	 * takes the text and extracts all the scoring characters.
	 * returns as a string with all the characters (occurences) in original order
	 */
	// ### to metoder med samme navn ###
	public String getScoringCharacters(String text) {
		String scoringCharacters = model.getScoringCharacters();
		String ret = "";
		//System.out.println("getScoringCharacters(). text = " + text);
		//System.out.println("getScoringCharacters(). scoringCharacters = " + scoringCharacters);
		for (int i=0; i<text.length(); i++) {
			if (scoringCharacters.indexOf(text.substring(i, i+1)) >= 0) {
				ret += text.substring(i, i+1);
			}
		}
		//System.out.println("getScoringCharacters() returns " + ret);
		return ret;
	}

}

