Index: /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/util/NameGenerator.java
===================================================================
--- /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/util/NameGenerator.java	(revision 26055)
+++ /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/util/NameGenerator.java	(revision 26055)
@@ -0,0 +1,298 @@
+package org.openstreetmap.josm.plugins.trustosm.util;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * This class is released under GNU general public license
+ * 
+ * Description: This class generates random names from syllables, and provides programmer a
+ * simple way to set a group of rules for generator to avoid unpronounceable and bizarre names.
+ * 
+ * SYLLABLE FILE REQUIREMENTS/FORMAT:
+ * 1) all syllables are separated by line break.
+ * 2) Syllable should not contain or start with whitespace, as this character is ignored and only first part of the syllable is read.
+ * 3) + and - characters are used to set rules, and using them in other way, may result in unpredictable results.
+ * 4) Empty lines are ignored.
+ * 
+ * SYLLABLE CLASSIFICATION:
+ * Name is usually composed from 3 different class of syllables, which include prefix, middle part and suffix.
+ * To declare syllable as a prefix in the file, insert "-" as a first character of the line.
+ * To declare syllable as a suffix in the file, insert "+" as a first character of the line.
+ * everything else is read as a middle part.
+ * 
+ * NUMBER OF SYLLABLES:
+ * Names may have any positive number of syllables. In case of 2 syllables, name will be composed from prefix and suffix.
+ * In case of 1 syllable, name will be chosen from amongst the prefixes.
+ * In case of 3 and more syllables, name will begin with prefix, is filled with middle parts and ended with suffix.
+ * 
+ * ASSIGNING RULES:
+ * I included a way to set 4 kind of rules for every syllable. To add rules to the syllables, write them right after the
+ * syllable and SEPARATE WITH WHITESPACE. (example: "aad +v -c"). The order of rules is not important.
+ * 
+ * RULES:
+ * 1) +v means that next syllable must definitely start with a vocal.
+ * 2) +c means that next syllable must definitely start with a consonant.
+ * 3) -v means that this syllable can only be added to another syllable, that ends with a vocal.
+ * 4) -c means that this syllable can only be added to another syllable, that ends with a consonant.
+ * So, our example: "aad +v -c" means that "aad" can only be after consonant and next syllable must start with vocal.
+ * Beware of creating logical mistakes, like providing only syllables ending with consonants, but expecting only vocals, which will be detected
+ * and RuntimeException will be thrown.
+ * 
+ * TO START:
+ * Create a new NameGenerator object, provide the syllable file, and create names using compose() method.
+ * 
+ * @author Joonas Vali, August 2009.
+ *
+ */
+public class NameGenerator {
+	ArrayList<String> pre = new ArrayList<String>();
+	ArrayList<String> mid = new ArrayList<String>();
+	ArrayList<String> sur = new ArrayList<String>();
+
+	final private static char[] vocals = {'a', 'e', 'i', 'o', 'u', 'ä', 'ö', 'õ', 'ü', 'y'};
+	final private static char[] consonants = {'b', 'c', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'p',	'q', 'r', 's', 't', 'v', 'w', 'x', 'y'};
+
+	private String fileName;
+
+	/**
+	 * Create new random name generator object. refresh() is automatically called.
+	 * @param fileName insert file name, where syllables are located
+	 * @throws IOException
+	 */
+	public NameGenerator(String fileName) throws IOException{
+		this.fileName = fileName;
+		refresh();
+	}
+
+	/**
+	 * Change the file. refresh() is automatically called during the process.
+	 * @param fileName insert the file name, where syllables are located.
+	 * @throws IOException
+	 */
+	public void changeFile(String fileName) throws IOException{
+		if(fileName == null) throw new IOException("File name cannot be null");
+		this.fileName = fileName;
+		refresh();
+	}
+
+	/**
+	 * Refresh names from file. No need to call that method, if you are not changing the file during the operation of program, as this method
+	 * is called every time file name is changed or new NameGenerator object created.
+	 * @throws IOException
+	 */
+	public void refresh() throws IOException{
+
+		FileReader input = null;
+		BufferedReader bufRead;
+		String line;
+
+		input = new FileReader(fileName);
+
+		bufRead = new BufferedReader(input);
+		line="";
+
+		while(line != null){
+			line = bufRead.readLine();
+			if(line != null && !line.equals("")){
+				if(line.charAt(0) == '-'){
+					pre.add(line.substring(1).toLowerCase());
+				}
+				else if(line.charAt(0) == '+'){
+					sur.add(line.substring(1).toLowerCase());
+				}
+				else{
+					mid.add(line.toLowerCase());
+				}
+			}
+		}
+		bufRead.close();
+	}
+
+	private String upper(String s){
+		return s.substring(0,1).toUpperCase().concat(s.substring(1));
+	}
+
+	private boolean containsConsFirst(ArrayList<String> array){
+		for(String s: array){
+			if(consonantFirst(s)) return true;
+		}
+		return false;
+	}
+
+	private boolean containsVocFirst(ArrayList<String> array){
+		for(String s: array){
+			if(vocalFirst(s)) return true;
+		}
+		return false;
+	}
+
+	private boolean allowCons(ArrayList<String> array){
+		for(String s: array){
+			if(hatesPreviousVocals(s) || hatesPreviousConsonants(s) == false) return true;
+		}
+		return false;
+	}
+
+	private boolean allowVocs(ArrayList<String> array){
+		for(String s: array){
+			if(hatesPreviousConsonants(s) || hatesPreviousVocals(s) == false) return true;
+		}
+		return false;
+	}
+
+	private boolean expectsVocal(String s){
+		if(s.substring(1).contains("+v")) return true;
+		else return false;
+	}
+	private boolean expectsConsonant(String s){
+		if(s.substring(1).contains("+c")) return true;
+		else return false;
+	}
+	private boolean hatesPreviousVocals(String s){
+		if(s.substring(1).contains("-c")) return true;
+		else return false;
+	}
+	private boolean hatesPreviousConsonants(String s){
+		if(s.substring(1).contains("-v")) return true;
+		else return false;
+	}
+
+	private String pureSyl(String s){
+		s = s.trim();
+		if(s.charAt(0) == '+' || s.charAt(0) == '-') s = s.substring(1);
+		return s.split(" ")[0];
+	}
+
+	private boolean vocalFirst(String s){
+		return (String.copyValueOf(vocals).contains(String.valueOf(s.charAt(0)).toLowerCase()));
+	}
+
+	private boolean consonantFirst(String s){
+		return (String.copyValueOf(consonants).contains(String.valueOf(s.charAt(0)).toLowerCase()));
+	}
+
+	private boolean vocalLast(String s){
+		return (String.copyValueOf(vocals).contains(String.valueOf(s.charAt(s.length()-1)).toLowerCase()));
+	}
+
+	private boolean consonantLast(String s){
+		return (String.copyValueOf(consonants).contains(String.valueOf(s.charAt(s.length()-1)).toLowerCase()));
+	}
+
+
+	/**
+	 * Compose a new name.
+	 * @param syls The number of syllables used in name.
+	 * @return Returns composed name as a String
+	 * @throws RuntimeException when logical mistakes are detected inside chosen file, and program is unable to complete the name.
+	 */
+	public String compose(int syls){
+		if(syls > 2 && mid.size() == 0) throw new RuntimeException("You are trying to create a name with more than 3 parts, which requires middle parts, " +
+				"which you have none in the file "+fileName+". You should add some. Every word, which doesn't have + or - for a prefix is counted as a middle part.");
+		if(pre.size() == 0) throw new RuntimeException("You have no prefixes to start creating a name. add some and use \"-\" prefix, to identify it as a prefix for a name. (example: -asd)");
+		if(sur.size() == 0) throw new RuntimeException("You have no suffixes to end a name. add some and use \"+\" prefix, to identify it as a suffix for a name. (example: +asd)");
+		if(syls < 1) throw new RuntimeException("compose(int syls) can't have less than 1 syllable");
+		int expecting = 0; // 1 for vocal, 2 for consonant
+		int last = 0; // 1 for vocal, 2 for consonant
+		String name;
+		int a = (int)(Math.random() * pre.size());
+
+		if(vocalLast(pureSyl(pre.get(a)))) last = 1;
+		else last = 2;
+
+		if(syls > 2){
+			if(expectsVocal(pre.get(a))){
+				expecting = 1;
+				if(containsVocFirst(mid) == false) throw new RuntimeException("Expecting \"middle\" part starting with vocal, " +
+				"but there is none. You should add one, or remove requirement for one.. ");
+			}
+			if(expectsConsonant(pre.get(a))){
+				expecting = 2;
+				if(containsConsFirst(mid) == false) throw new RuntimeException("Expecting \"middle\" part starting with consonant, " +
+				"but there is none. You should add one, or remove requirement for one.. ");
+			}
+		}
+		else{
+			if(expectsVocal(pre.get(a))){
+				expecting = 1;
+				if(containsVocFirst(sur) == false) throw new RuntimeException("Expecting \"suffix\" part starting with vocal, " +
+				"but there is none. You should add one, or remove requirement for one.. ");
+			}
+			if(expectsConsonant(pre.get(a))){
+				expecting = 2;
+				if(containsConsFirst(sur) == false) throw new RuntimeException("Expecting \"suffix\" part starting with consonant, " +
+				"but there is none. You should add one, or remove requirement for one.. ");
+			}
+		}
+		if(vocalLast(pureSyl(pre.get(a))) && allowVocs(mid) == false) throw new RuntimeException("Expecting \"middle\" part that allows last character of prefix to be a vocal, " +
+				"but there is none. You should add one, or remove requirements that cannot be fulfilled.. the prefix used, was : \""+pre.get(a)+"\", which" +
+		"means there should be a part available, that has \"-v\" requirement or no requirements for previous syllables at all.");
+
+		if(consonantLast(pureSyl(pre.get(a))) && allowCons(mid) == false) throw new RuntimeException("Expecting \"middle\" part that allows last character of prefix to be a consonant, " +
+				"but there is none. You should add one, or remove requirements that cannot be fulfilled.. the prefix used, was : \""+pre.get(a)+"\", which" +
+		"means there should be a part available, that has \"-c\" requirement or no requirements for previous syllables at all.");
+
+		int b[] = new int[syls];
+		for(int i = 0; i<b.length-2; i++){
+
+			do{
+				b[i] = (int)(Math.random() * mid.size());
+				//System.out.println("exp " +expecting+" vocalF:"+vocalFirst(mid.get(b[i]))+" syl: "+mid.get(b[i]));
+			}
+			while(expecting == 1 && vocalFirst(pureSyl(mid.get(b[i]))) == false || expecting == 2 && consonantFirst(pureSyl(mid.get(b[i]))) == false
+					|| last == 1 && hatesPreviousVocals(mid.get(b[i])) || last == 2 && hatesPreviousConsonants(mid.get(b[i])));
+
+			expecting = 0;
+			if(expectsVocal(mid.get(b[i]))){
+				expecting = 1;
+				if(i < b.length-3 && containsVocFirst(mid) == false) throw new RuntimeException("Expecting \"middle\" part starting with vocal, " +
+				"but there is none. You should add one, or remove requirement for one.. ");
+				if(i == b.length-3 && containsVocFirst(sur) == false) throw new RuntimeException("Expecting \"suffix\" part starting with vocal, " +
+				"but there is none. You should add one, or remove requirement for one.. ");
+			}
+			if(expectsConsonant(mid.get(b[i]))){
+				expecting = 2;
+				if(i < b.length-3 && containsConsFirst(mid) == false) throw new RuntimeException("Expecting \"middle\" part starting with consonant, " +
+				"but there is none. You should add one, or remove requirement for one.. ");
+				if(i == b.length-3 && containsConsFirst(sur) == false) throw new RuntimeException("Expecting \"suffix\" part starting with consonant, " +
+				"but there is none. You should add one, or remove requirement for one.. ");
+			}
+			if(vocalLast(pureSyl(mid.get(b[i]))) && allowVocs(mid) == false && syls > 3) throw new RuntimeException("Expecting \"middle\" part that allows last character of last syllable to be a vocal, " +
+					"but there is none. You should add one, or remove requirements that cannot be fulfilled.. the part used, was : \""+mid.get(b[i])+"\", which " +
+			"means there should be a part available, that has \"-v\" requirement or no requirements for previous syllables at all.");
+
+			if(consonantLast(pureSyl(mid.get(b[i]))) && allowCons(mid) == false && syls > 3) throw new RuntimeException("Expecting \"middle\" part that allows last character of last syllable to be a consonant, " +
+					"but there is none. You should add one, or remove requirements that cannot be fulfilled.. the part used, was : \""+mid.get(b[i])+"\", which " +
+			"means there should be a part available, that has \"-c\" requirement or no requirements for previous syllables at all.");
+			if(i == b.length-3){
+				if(vocalLast(pureSyl(mid.get(b[i]))) && allowVocs(sur) == false) throw new RuntimeException("Expecting \"suffix\" part that allows last character of last syllable to be a vocal, " +
+						"but there is none. You should add one, or remove requirements that cannot be fulfilled.. the part used, was : \""+mid.get(b[i])+"\", which " +
+				"means there should be a suffix available, that has \"-v\" requirement or no requirements for previous syllables at all.");
+
+				if(consonantLast(pureSyl(mid.get(b[i]))) && allowCons(sur) == false) throw new RuntimeException("Expecting \"suffix\" part that allows last character of last syllable to be a consonant, " +
+						"but there is none. You should add one, or remove requirements that cannot be fulfilled.. the part used, was : \""+mid.get(b[i])+"\", which " +
+				"means there should be a suffix available, that has \"-c\" requirement or no requirements for previous syllables at all.");
+			}
+			if(vocalLast(pureSyl(mid.get(b[i])))) last = 1;
+			else last = 2;
+		}
+
+		int c;
+		do{
+			c = (int)(Math.random() * sur.size());
+		}
+		while(expecting == 1 && vocalFirst(pureSyl(sur.get(c))) == false || expecting == 2 && consonantFirst(pureSyl(sur.get(c))) == false
+				|| last == 1 && hatesPreviousVocals(sur.get(c)) || last == 2 && hatesPreviousConsonants(sur.get(c)));
+
+		name = upper(pureSyl(pre.get(a).toLowerCase()));
+		for(int i = 0; i<b.length-2; i++){
+			name = name.concat(pureSyl(mid.get(b[i]).toLowerCase()));
+		}
+		if(syls > 1)
+			name = name.concat(pureSyl(sur.get(c).toLowerCase()));
+		return name;
+	}
+}
Index: /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/util/SpringUtilities.java
===================================================================
--- /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/util/SpringUtilities.java	(revision 26055)
+++ /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/util/SpringUtilities.java	(revision 26055)
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   - Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *
+ *   - Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ *   - Neither the name of Oracle or the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */ 
+
+package org.openstreetmap.josm.plugins.trustosm.util;
+
+import javax.swing.*;
+import javax.swing.SpringLayout;
+import java.awt.*;
+
+/**
+ * A 1.4 file that provides utility methods for
+ * creating form- or grid-style layouts with SpringLayout.
+ * These utilities are used by several programs, such as
+ * SpringBox and SpringCompactGrid.
+ */
+public class SpringUtilities {
+    /**
+     * A debugging utility that prints to stdout the component's
+     * minimum, preferred, and maximum sizes.
+     */
+    public static void printSizes(Component c) {
+        System.out.println("minimumSize = " + c.getMinimumSize());
+        System.out.println("preferredSize = " + c.getPreferredSize());
+        System.out.println("maximumSize = " + c.getMaximumSize());
+    }
+
+    /**
+     * Aligns the first <code>rows</code> * <code>cols</code>
+     * components of <code>parent</code> in
+     * a grid. Each component is as big as the maximum
+     * preferred width and height of the components.
+     * The parent is made just big enough to fit them all.
+     *
+     * @param rows number of rows
+     * @param cols number of columns
+     * @param initialX x location to start the grid at
+     * @param initialY y location to start the grid at
+     * @param xPad x padding between cells
+     * @param yPad y padding between cells
+     */
+    public static void makeGrid(Container parent,
+                                int rows, int cols,
+                                int initialX, int initialY,
+                                int xPad, int yPad) {
+        SpringLayout layout;
+        try {
+            layout = (SpringLayout)parent.getLayout();
+        } catch (ClassCastException exc) {
+            System.err.println("The first argument to makeGrid must use SpringLayout.");
+            return;
+        }
+
+        Spring xPadSpring = Spring.constant(xPad);
+        Spring yPadSpring = Spring.constant(yPad);
+        Spring initialXSpring = Spring.constant(initialX);
+        Spring initialYSpring = Spring.constant(initialY);
+        int max = rows * cols;
+
+        //Calculate Springs that are the max of the width/height so that all
+        //cells have the same size.
+        Spring maxWidthSpring = layout.getConstraints(parent.getComponent(0)).
+                                    getWidth();
+        Spring maxHeightSpring = layout.getConstraints(parent.getComponent(0)).
+                                    getWidth();
+        for (int i = 1; i < max; i++) {
+            SpringLayout.Constraints cons = layout.getConstraints(
+                                            parent.getComponent(i));
+
+            maxWidthSpring = Spring.max(maxWidthSpring, cons.getWidth());
+            maxHeightSpring = Spring.max(maxHeightSpring, cons.getHeight());
+        }
+
+        //Apply the new width/height Spring. This forces all the
+        //components to have the same size.
+        for (int i = 0; i < max; i++) {
+            SpringLayout.Constraints cons = layout.getConstraints(
+                                            parent.getComponent(i));
+
+            cons.setWidth(maxWidthSpring);
+            cons.setHeight(maxHeightSpring);
+        }
+
+        //Then adjust the x/y constraints of all the cells so that they
+        //are aligned in a grid.
+        SpringLayout.Constraints lastCons = null;
+        SpringLayout.Constraints lastRowCons = null;
+        for (int i = 0; i < max; i++) {
+            SpringLayout.Constraints cons = layout.getConstraints(
+                                                 parent.getComponent(i));
+            if (i % cols == 0) { //start of new row
+                lastRowCons = lastCons;
+                cons.setX(initialXSpring);
+            } else { //x position depends on previous component
+                cons.setX(Spring.sum(lastCons.getConstraint(SpringLayout.EAST),
+                                     xPadSpring));
+            }
+
+            if (i / cols == 0) { //first row
+                cons.setY(initialYSpring);
+            } else { //y position depends on previous row
+                cons.setY(Spring.sum(lastRowCons.getConstraint(SpringLayout.SOUTH),
+                                     yPadSpring));
+            }
+            lastCons = cons;
+        }
+
+        //Set the parent's size.
+        SpringLayout.Constraints pCons = layout.getConstraints(parent);
+        pCons.setConstraint(SpringLayout.SOUTH,
+                            Spring.sum(
+                                Spring.constant(yPad),
+                                lastCons.getConstraint(SpringLayout.SOUTH)));
+        pCons.setConstraint(SpringLayout.EAST,
+                            Spring.sum(
+                                Spring.constant(xPad),
+                                lastCons.getConstraint(SpringLayout.EAST)));
+    }
+
+    /* Used by makeCompactGrid. */
+    private static SpringLayout.Constraints getConstraintsForCell(
+                                                int row, int col,
+                                                Container parent,
+                                                int cols) {
+        SpringLayout layout = (SpringLayout) parent.getLayout();
+        Component c = parent.getComponent(row * cols + col);
+        return layout.getConstraints(c);
+    }
+
+    /**
+     * Aligns the first <code>rows</code> * <code>cols</code>
+     * components of <code>parent</code> in
+     * a grid. Each component in a column is as wide as the maximum
+     * preferred width of the components in that column;
+     * height is similarly determined for each row.
+     * The parent is made just big enough to fit them all.
+     *
+     * @param rows number of rows
+     * @param cols number of columns
+     * @param initialX x location to start the grid at
+     * @param initialY y location to start the grid at
+     * @param xPad x padding between cells
+     * @param yPad y padding between cells
+     */
+    public static void makeCompactGrid(Container parent,
+                                       int rows, int cols,
+                                       int initialX, int initialY,
+                                       int xPad, int yPad) {
+        SpringLayout layout;
+        try {
+            layout = (SpringLayout)parent.getLayout();
+        } catch (ClassCastException exc) {
+            System.err.println("The first argument to makeCompactGrid must use SpringLayout.");
+            return;
+        }
+
+        //Align all cells in each column and make them the same width.
+        Spring x = Spring.constant(initialX);
+        for (int c = 0; c < cols; c++) {
+            Spring width = Spring.constant(0);
+            for (int r = 0; r < rows; r++) {
+                width = Spring.max(width,
+                                   getConstraintsForCell(r, c, parent, cols).
+                                       getWidth());
+            }
+            for (int r = 0; r < rows; r++) {
+                SpringLayout.Constraints constraints =
+                        getConstraintsForCell(r, c, parent, cols);
+                constraints.setX(x);
+                constraints.setWidth(width);
+            }
+            x = Spring.sum(x, Spring.sum(width, Spring.constant(xPad)));
+        }
+
+        //Align all cells in each row and make them the same height.
+        Spring y = Spring.constant(initialY);
+        for (int r = 0; r < rows; r++) {
+            Spring height = Spring.constant(0);
+            for (int c = 0; c < cols; c++) {
+                height = Spring.max(height,
+                                    getConstraintsForCell(r, c, parent, cols).
+                                        getHeight());
+            }
+            for (int c = 0; c < cols; c++) {
+                SpringLayout.Constraints constraints =
+                        getConstraintsForCell(r, c, parent, cols);
+                constraints.setY(y);
+                constraints.setHeight(height);
+            }
+            y = Spring.sum(y, Spring.sum(height, Spring.constant(yPad)));
+        }
+
+        //Set the parent's size.
+        SpringLayout.Constraints pCons = layout.getConstraints(parent);
+        pCons.setConstraint(SpringLayout.SOUTH, y);
+        pCons.setConstraint(SpringLayout.EAST, x);
+    }
+}
Index: /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/util/TrustAnalyzer.java
===================================================================
--- /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/util/TrustAnalyzer.java	(revision 26055)
+++ /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/util/TrustAnalyzer.java	(revision 26055)
@@ -0,0 +1,243 @@
+package org.openstreetmap.josm.plugins.trustosm.util;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.JOptionPane;
+
+import org.bouncycastle.openpgp.PGPSignature;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.plugins.trustosm.TrustOSMplugin;
+import org.openstreetmap.josm.plugins.trustosm.data.TrustNode;
+import org.openstreetmap.josm.plugins.trustosm.data.TrustOsmPrimitive;
+import org.openstreetmap.josm.plugins.trustosm.data.TrustRelation;
+import org.openstreetmap.josm.plugins.trustosm.data.TrustSignatures;
+import org.openstreetmap.josm.plugins.trustosm.data.TrustWay;
+
+public class TrustAnalyzer {
+
+	public static void showManipulationWarning(){
+		JOptionPane.showMessageDialog(Main.parent, tr("The Signature is broken!"), tr("Manipulation Warning"), JOptionPane.WARNING_MESSAGE);
+	}
+
+
+	public static double computeReputation(TrustOsmPrimitive trust, Map<String, List<PGPSignature>> textsigs) {
+		/** just for simplicity - count all valid sigs */
+		int count = 0;
+		for (List<PGPSignature> siglist : textsigs.values()) {
+			count += siglist.size();
+		}
+		return count;
+	}
+
+	public static boolean isTagRatingValid(TrustOsmPrimitive trust, String key, String signedPlaintext) {
+		/** Rating is valid if signed plaintext matches the current plaintext */
+		String currentSigtext = TrustOsmPrimitive.generateTagSigtext(trust.getOsmPrimitive(),key);
+		return currentSigtext.equals(signedPlaintext);
+	}
+
+
+	public static void checkTag(TrustOsmPrimitive trust, String key) {
+		Map<String, List<PGPSignature>> validRatings = new HashMap<String, List<PGPSignature>>();
+
+		TrustSignatures sigs;
+		if ((sigs = trust.getSigsOnKey(key))!=null) {
+			for (PGPSignature sig : sigs.getSignatures()) {
+				/** Here we have a full rating
+				 *  The first question: Is the Signature valid?
+				 *  It could be manipulated...
+				 * */
+				String signedPlaintext = sigs.getSigtext(sig);
+				if (TrustOSMplugin.gpg.verify(signedPlaintext, sig)) {
+					/** If it is valid...
+					 * Second question: Is the rating valid?
+					 */
+					if (isTagRatingValid(trust,key,signedPlaintext)) {
+						/** if the rating is good, we can try to compute a reputation value at the end
+						 *  so we save the important rating stuff
+						 */
+						if (validRatings.containsKey(signedPlaintext)) {
+							validRatings.get(signedPlaintext).add(sig);
+						} else {
+							List<PGPSignature> l = new ArrayList<PGPSignature>();
+							l.add(sig);
+							validRatings.put(signedPlaintext, l);
+						}
+
+						//if (sigs.getStatus() == TrustSignatures.SIG_UNKNOWN) sigs.setStatus(TrustSignatures.SIG_VALID);
+					} else {
+						//sigs.setStatus(TrustSignatures.SIG_BROKEN);
+					}
+				} else {
+					//sigs.setStatus(TrustSignatures.SIG_BROKEN);
+					showManipulationWarning();
+				}
+			}
+			/** now we know which ratings are valid to compute a reputation */
+			sigs.setReputation(computeReputation(trust, validRatings));
+			/** if all available signatures are valid we can set the TrustSignatures status to valid */
+			System.out.println(validRatings.size()+":"+sigs.countSigs());
+			if (validRatings.size() == 1) sigs.setStatus(TrustSignatures.SIG_VALID);
+			else sigs.setStatus(TrustSignatures.SIG_BROKEN);
+		}
+	}
+
+
+	public static boolean isNodeRatingValid(TrustNode trust, String signedPlaintext, PGPSignature sig) {
+		/** Rating is valid if Node from signed plaintext is inside Tolerance given in Signature */
+		Node signedNode = TrustNode.generateNodeFromSigtext(signedPlaintext);
+		Node currentNode = (Node)trust.getOsmPrimitive();
+		double dist = signedNode.getCoor().greatCircleDistance(currentNode.getCoor());
+
+		/** is distance between signed Node and current Node inside tolerance? */
+		return dist<=TrustGPG.searchTolerance(sig);
+	}
+
+	/**
+	 * Check if the ratings made for a Node are valid for the current position of that node
+	 * and compute reputation.
+	 * @param trust	The current TrustNode with its ratings
+	 */
+	public static void checkNode(TrustNode trust) {
+		Map<String, List<PGPSignature>> validRatings = new HashMap<String, List<PGPSignature>>();
+		Node node = (Node)trust.getOsmPrimitive();
+		TrustSignatures sigs;
+		if ((sigs = trust.getNodeSigs())!=null) {
+			for (String signedPlaintext : sigs.getAllPlainTexts()) {
+				for (PGPSignature sig : sigs.getSignaturesByPlaintext(signedPlaintext)) {
+					/** first thing: check signature */
+					if (TrustOSMplugin.gpg.verify(signedPlaintext,sig)) {
+						/** if signature is valid check rating */
+						if (isNodeRatingValid(trust,signedPlaintext,sig)) {
+							/** if the rating is good, we can try to compute a reputation value at the end
+							 *  so we save the important rating stuff
+							 */
+							if (validRatings.containsKey(signedPlaintext)) {
+								validRatings.get(signedPlaintext).add(sig);
+							} else {
+								List<PGPSignature> l = new ArrayList<PGPSignature>();
+								l.add(sig);
+								validRatings.put(signedPlaintext, l);
+							}
+
+							//if (sigs.getStatus() == TrustSignatures.SIG_UNKNOWN) sigs.setStatus(TrustSignatures.SIG_VALID);
+						} else {
+							//sigs.setStatus(TrustSignatures.SIG_BROKEN);
+						}
+
+					} else {
+						//sigs.setStatus(TrustSignatures.SIG_BROKEN);
+						showManipulationWarning();
+					}
+				}
+			}
+			/** now we know which ratings are valid to compute a reputation */
+			sigs.setReputation(computeReputation(trust, validRatings));
+			/** if all available signatures are valid we can set the TrustSignatures status to valid */
+			if (validRatings.size() == 1) sigs.setStatus(TrustSignatures.SIG_VALID);
+			else sigs.setStatus(TrustSignatures.SIG_BROKEN);
+		}
+	}
+
+	/**
+	 * Check if the ratings made for a specific WaySegment are valid for the current form of that WaySegment
+	 * @param trust
+	 * @param seg
+	 * @param signedPlaintext
+	 * @param sig
+	 * @return
+	 */
+	public static boolean isSegmentRatingValid(TrustWay trust, List<Node> nodes, String signedPlaintext, PGPSignature sig) {
+		/** Rating is valid if Nodes from Segment of signed plaintext are inside Tolerance given in Signature */
+		List<Node> signedSegment = TrustWay.generateSegmentFromSigtext(signedPlaintext);
+
+		double tolerance = TrustGPG.searchTolerance(sig);
+
+		for (int i = 0; i<2; i++){
+			Node signedNode = signedSegment.get(i);
+			Node currentNode = nodes.get(i);
+			double dist = signedNode.getCoor().greatCircleDistance(currentNode.getCoor());
+			if (dist>tolerance) return false;
+		}
+		return true;
+	}
+
+	/**
+	 * Check if there are ratings for a current WaySegment of a TrustWay
+	 * and if so, compute Reputation
+	 * @param trust the current TrustWay
+	 * @param seg the current WaySegment to check for reputation
+	 */
+	public static void checkSegment(TrustWay trust, List<Node> nodes) {
+		Map<String, List<PGPSignature>> validRatings = new HashMap<String, List<PGPSignature>>();
+
+		TrustSignatures sigs;
+		if ((sigs = trust.getSigsOnSegment(nodes))!=null) {
+			for (String signedPlaintext : sigs.getAllPlainTexts()) {
+				for (PGPSignature sig : sigs.getSignaturesByPlaintext(signedPlaintext)) {
+					/** first thing: check signature */
+					if (TrustOSMplugin.gpg.verify(signedPlaintext,sig)) {
+						/** if signature is valid check rating */
+						if (isSegmentRatingValid(trust,nodes,signedPlaintext,sig)) {
+							/** if the rating is good, we can try to compute a reputation value at the end
+							 *  so we save the important rating stuff
+							 */
+							if (validRatings.containsKey(signedPlaintext)) {
+								validRatings.get(signedPlaintext).add(sig);
+							} else {
+								List<PGPSignature> l = new ArrayList<PGPSignature>();
+								l.add(sig);
+								validRatings.put(signedPlaintext, l);
+							}
+
+							//if (sigs.getStatus() == TrustSignatures.SIG_UNKNOWN) sigs.setStatus(TrustSignatures.SIG_VALID);
+						} else {
+							//sigs.setStatus(TrustSignatures.SIG_BROKEN);
+						}
+
+					} else {
+						//sigs.setStatus(TrustSignatures.SIG_BROKEN);
+						showManipulationWarning();
+					}
+				}
+			}
+			/** now we know which ratings are valid to compute a reputation */
+			sigs.setReputation(computeReputation(trust, validRatings));
+			/** if all available signatures are valid we can set the TrustSignatures status to valid */
+			if (validRatings.size() == sigs.countSigs()) sigs.setStatus(TrustSignatures.SIG_VALID);
+			else sigs.setStatus(TrustSignatures.SIG_BROKEN);
+		}
+	}
+
+
+	public static void checkEverything(TrustOsmPrimitive trust) {
+		/** check every single tag for reputation */
+		for (String key : trust.getSignedKeys()){
+			checkTag(trust, key);
+		}
+		if (trust instanceof TrustNode) {
+			/** check all reputation of this single Node */
+			checkNode((TrustNode) trust);
+		} else if (trust instanceof TrustWay){
+			TrustWay tw = (TrustWay) trust;
+			/** check all reputation for every Segment of this Way */
+			List<Node> wayNodes = ((Way)tw.getOsmPrimitive()).getNodes();
+			for (int i=0; i<wayNodes.size()-1; i++) {
+				List<Node> nodes = new ArrayList<Node>();
+				nodes.add(wayNodes.get(i));
+				nodes.add(wayNodes.get(i+1));
+				checkSegment(tw,nodes);
+			}
+
+		} else if (trust instanceof TrustRelation){
+			TrustRelation tr = (TrustRelation) trust;
+		}
+
+	}
+}
