Index: /applications/editors/josm/plugins/czechaddress/build.xml
===================================================================
--- /applications/editors/josm/plugins/czechaddress/build.xml	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/build.xml	(revision 15166)
@@ -0,0 +1,102 @@
+<project name="czechaddress" default="dist" basedir=".">
+    <property name="josm.base"              value="../../core"/>
+    <property name="josm.jar"               value="${josm.base}/dist/josm-custom.jar"/>
+    <property name="josm.doc.dir"           value="${josm.base}/doc"/>
+    <property name="plugin.dist.dir"        value="../../dist"/>
+    <property name="plugin.build.dir"       value="build"/>
+    <property name="plugin.javadoc.dir"     value="doc"/>
+    <property name="plugin.basepackage"     value="org.openstreetmap.josm.plugins.czechaddress"/>
+    <property name="plugin.basepackage.dir" value="org/openstreetmap/josm/plugins/czechaddress"/>
+    <property name="plugin.jar"             value="${plugin.dist.dir}/${ant.project.name}.jar"/>
+    <property name="ant.build.javac.target" value="1.5"/>
+
+    <target name="clean">
+        <delete dir="${plugin.build.dir}"/> 
+        <delete dir="${plugin.jar}"/> 
+    </target>
+    <target name="init">
+        <mkdir dir="${plugin.build.dir}"/>
+        <mkdir dir="${plugin.build.dir}"/>
+    </target>
+
+    <target name="compile"
+            depends="init"
+            description="Compile the plugin">
+                
+        <!-- compile the plugin -->
+        <javac srcdir="src"
+               classpath="${josm.jar}"
+               destdir="${plugin.build.dir}"
+               debug="true"/>
+
+        <!-- create the manifest -->
+        <manifest file="${plugin.build.dir}/${plugin.basepackage.dir}/MANIFEST.MF">
+            <attribute name="Author" value="Radomír Černoch"/>
+            <attribute name="Plugin-Description" value="Creating and handling address nodes and buildings within Czech Republic."/>
+            <attribute name="Plugin-Mainversion" value="1566"/>
+            <attribute name="Plugin-Version" value="0.1"/>
+            <attribute name="Plugin-Class" value="${plugin.basepackage}.CzechAddressPlugin"/>
+        </manifest>
+
+        <!-- include the images -->
+        <copy todir="${plugin.build.dir}/images">
+            <fileset dir="images"/>
+        </copy>
+    </target>
+
+    <target name="dist"
+            depends="compile"
+            description="Create the .jar file for distribution">
+        <mkdir dir="${plugin.dist.dir}"/>
+        <jar destfile="${plugin.jar}"
+             basedir="${plugin.build.dir}"
+             manifest="${plugin.build.dir}/${plugin.basepackage.dir}/MANIFEST.MF"/>
+    </target>
+
+    <target name="doc" description="Create Javadoc API documentation">
+        <ant antfile="build.xml" target="doc" dir="${josm.base}"/>
+        <mkdir dir="${plugin.javadoc.dir}"/>
+        <javadoc sourcepath="src"
+                 packagenames="*"
+                 destdir="${plugin.javadoc.dir}"
+                 use="true"
+                 charset="UTF-8">
+            <doctitle><![CDATA[Czech Address JOSM plugin]]></doctitle>
+            <bottom><![CDATA[<i>Licenced under GPLv3. Bugreports should be sent to
+                    <a href='mailto:radomir.cernoch@gmail.com'>Radomír Černoch</a></i>]]>
+            </bottom>
+            <!--<tag name="todo" scope="all" description="To do:"/>-->
+            <link href="http://java.sun.com/j2se/1.5.0/docs/api/"/>
+            <link href="http://developer.java.sun.com/developer/products/xml/docs/api/"/>
+            <!--<link href="file://${user.home}/devel/core/doc"/>-->
+        </javadoc>
+    </target>
+
+    <target name="install" depends="dist">
+        <property environment="env"/>
+        <condition property="josm.plugins.dir"
+                   value="${env.APPDATA}/JOSM/plugins"
+                   else="${user.home}/.josm/plugins">
+            <and><os family="windows"/></and>
+        </condition>
+        <copy file="${plugin.jar}" todir="${josm.plugins.dir}"/>
+    </target>
+
+    <!-- Before running "run" target, please "ant dist" the JOSM. -->
+    <target name="run" depends="compile">
+        <java classname="JOSM" fork="true">
+            <jvmarg value="-Xmx1024m"/>
+            <jvmarg value="-Xdebug"/>
+            <jvmarg value="-ea"/>
+            <classpath>
+                <pathelement location="${plugin.build.dir}"/>
+                <pathelement path="${java.class.path}"/>
+            </classpath>
+            <classpath>
+                <pathelement location="${josm.jar}"/>
+                <pathelement path="${java.class.path}"/>
+            </classpath>
+        </java>
+    </target>
+
+</project>
Index: /applications/editors/josm/plugins/czechaddress/nbproject/project.xml
===================================================================
--- /applications/editors/josm/plugins/czechaddress/nbproject/project.xml	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/nbproject/project.xml	(revision 15166)
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://www.netbeans.org/ns/project/1">
+    <type>org.netbeans.modules.ant.freeform</type>
+    <configuration>
+        <general-data xmlns="http://www.netbeans.org/ns/freeform-project/1">
+            <name>czechaddress</name>
+            <folders>
+                <source-folder>
+                    <label>src</label>
+                    <type>java</type>
+                    <location>src</location>
+                </source-folder>
+            </folders>
+            <ide-actions>
+                <action name="build">
+                    <target>dist</target>
+                </action>
+                <action name="clean">
+                    <target>clean</target>
+                </action>
+                <action name="javadoc">
+                    <target>doc</target>
+                </action>
+                <action name="run">
+                    <target>run</target>
+                </action>
+                <action name="rebuild">
+                    <target>clean</target>
+                    <target>dist</target>
+                </action>
+            </ide-actions>
+            <view>
+                <items>
+                    <source-folder style="packages">
+                        <label>src</label>
+                        <location>src</location>
+                    </source-folder>
+                </items>
+                <context-menu>
+                    <ide-action name="build"/>
+                    <ide-action name="clean"/>
+                    <ide-action name="javadoc"/>
+                    <ide-action name="run"/>
+                    <ide-action name="rebuild"/>
+                </context-menu>
+            </view>
+            <subprojects/>
+        </general-data>
+        <general-data xmlns="http://www.netbeans.org/ns/freeform-project/2">
+            <!-- Do not use Project Properties customizer when editing this file manually. -->
+            <name>czechaddress</name>
+            <properties/>
+            <folders>
+                <source-folder>
+                    <label>czechaddress</label>
+                    <location>.</location>
+                    <encoding>UTF-8</encoding>
+                </source-folder>
+                <source-folder>
+                    <label>src</label>
+                    <type>java</type>
+                    <location>src</location>
+                    <encoding>UTF-8</encoding>
+                </source-folder>
+            </folders>
+            <ide-actions>
+                <action name="build">
+                    <target>compile</target>
+                </action>
+                <action name="clean">
+                    <target>clean</target>
+                </action>
+                <action name="javadoc">
+                    <target>doc</target>
+                </action>
+                <action name="run">
+                    <target>run</target>
+                </action>
+                <action name="rebuild">
+                    <target>clean</target>
+                    <target>compile</target>
+                </action>
+                <action name="debug">
+                    <script>nbproject/ide-targets.xml</script>
+                    <target>debug-nb</target>
+                </action>
+            </ide-actions>
+            <export>
+                <type>folder</type>
+                <location>dist</location>
+                <build-target>compile</build-target>
+            </export>
+            <view>
+                <items>
+                    <source-folder style="packages">
+                        <label>src</label>
+                        <location>src</location>
+                    </source-folder>
+                    <source-file>
+                        <location>build.xml</location>
+                    </source-file>
+                </items>
+                <context-menu>
+                    <ide-action name="build"/>
+                    <ide-action name="rebuild"/>
+                    <ide-action name="clean"/>
+                    <ide-action name="javadoc"/>
+                    <ide-action name="run"/>
+                    <ide-action name="debug"/>
+                </context-menu>
+            </view>
+            <subprojects/>
+        </general-data>
+        <java-data xmlns="http://www.netbeans.org/ns/freeform-project-java/2">
+            <compilation-unit>
+                <package-root>src</package-root>
+                <classpath mode="compile">../../core/build:../../core/src</classpath>
+                <javadoc-built-to>doc</javadoc-built-to>
+                <source-level>1.5</source-level>
+            </compilation-unit>
+        </java-data>
+    </configuration>
+</project>
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/CzechAddressPlugin.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/CzechAddressPlugin.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/CzechAddressPlugin.java	(revision 15166)
@@ -0,0 +1,198 @@
+package org.openstreetmap.josm.plugins.czechaddress;
+
+import java.awt.event.KeyEvent;
+import org.openstreetmap.josm.plugins.czechaddress.actions.PointManipulatorAction;
+import org.openstreetmap.josm.plugins.czechaddress.gui.LocationSelector;
+import org.openstreetmap.josm.plugins.czechaddress.actions.GroupManipulatorAction;
+import org.openstreetmap.josm.plugins.czechaddress.gui.ConflictResolver;
+import java.util.ArrayList;
+import java.util.List;
+import javax.swing.AbstractButton;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+
+import org.openstreetmap.josm.plugins.czechaddress.addressdatabase.Database;
+import org.openstreetmap.josm.plugins.czechaddress.addressdatabase.ElementWithStreets;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.IconToggleButton;
+import org.openstreetmap.josm.gui.MainMenu;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.plugins.Plugin;
+import org.openstreetmap.josm.plugins.czechaddress.actions.ConflictResolveAction;
+import org.openstreetmap.josm.plugins.czechaddress.addressdatabase.AddressElement;
+import org.openstreetmap.josm.plugins.czechaddress.addressdatabase.House;
+import org.openstreetmap.josm.plugins.czechaddress.parser.MvcrParser;
+import org.openstreetmap.josm.plugins.czechaddress.actions.FactoryAction;
+import org.openstreetmap.josm.plugins.czechaddress.actions.SplitAreaByEmptyWayAction;
+import org.openstreetmap.josm.plugins.czechaddress.gui.FactoryDialog;
+import org.openstreetmap.josm.plugins.czechaddress.intelligence.Reasoner;
+import org.openstreetmap.josm.plugins.czechaddress.intelligence.SelectionMonitor;
+
+/**
+ * Plugin for handling address nodes within the Czech Republic.
+ * 
+ * @author Radomír Černoch, radomir.cernoch@gmail.com
+ */
+public class CzechAddressPlugin extends Plugin implements StatusListener {
+
+
+    JMenu czechMenu;
+    List<JMenuItem> menuItems = new ArrayList<JMenuItem>();
+
+    List<AbstractButton> pluginButtons =
+            new ArrayList<AbstractButton>();
+
+    static private Reasoner mainReasoner = null;
+    static private Database mainDatabase = null;
+
+    public SelectionMonitor        selectionMonitor = null;
+    static public FactoryDialog    factoryDialog    = null;
+    static public ConflictResolver conflictResolver = null;
+
+    static private String pluginDir = null;
+
+    public CzechAddressPlugin() {
+
+        pluginDir = getPluginDir();
+
+        factoryDialog    = FactoryDialog.getInstance();
+        conflictResolver = new ConflictResolver();
+
+        addStatusListener(this);
+        addStatusListener(conflictResolver);
+
+        MainMenu.add(Main.main.menu.toolsMenu, new SplitAreaByEmptyWayAction());
+
+        // Prepare for filling the database.
+        mainDatabase = new Database();
+        final MvcrParser parser = new MvcrParser();
+        //parser.setFilter(null, null, null, "");
+        //parser.setFilter("BRNO", "BRNO", null, null);
+        parser.setTargetDatabase(mainDatabase);
+        parser.setStorageDir(pluginDir);
+
+        // Fill the database in separate thread.
+        Thread t = new Thread() { @Override public void run() {
+            super.run();
+            try {
+               parser.fillDatabase();
+               broadcastStatusChanged(StatusListener.MESSAGE_DATABASE_LOADED);
+            } catch (DatabaseLoadException dle) {
+               dle.printStackTrace();
+               System.err.println("CzechAddress: " +
+                            "Selhalo načtení databáze. Plugin je neaktivní.");
+            }
+        }};
+
+        t.setPriority(Thread.MIN_PRIORITY);
+        t.start();
+    }
+
+    @Override
+    public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {
+
+        super.mapFrameInitialized(oldFrame, newFrame);
+
+        if (newFrame == null)
+            return;
+        
+        newFrame.addToggleDialog(FactoryDialog.getInstance());
+        newFrame.addMapMode(new IconToggleButton(new FactoryAction(newFrame)));
+    }
+
+    public static Reasoner getReasoner() {
+        return mainReasoner;
+    }
+
+    public static Database getDatabase() {
+        return mainDatabase;
+    }
+
+    static public void initReasoner() {
+
+        // Move houses from list of Houses to list of AddressElements.
+        List<House> tmp1 = location.getAllHouses();
+        ArrayList<AddressElement> tmp2 = new ArrayList<AddressElement>(tmp1.size());
+        for (House h : tmp1) tmp2.add(h);
+
+        // And add them to the reasoner.
+        mainReasoner = new Reasoner(tmp2);
+        mainReasoner.addPrimitives(Main.ds.allPrimitives());
+    }
+
+    static private ElementWithStreets location = null;
+
+    static public ElementWithStreets getLocation() {
+        if (location == null)
+            changeLocation();
+
+        return location;
+    }
+
+    static public void changeLocation() {
+        ElementWithStreets newLocation = LocationSelector.selectLocation();
+
+        if (newLocation != null && newLocation != location) {
+            location = newLocation;
+
+            /*
+            // Parse the database once more to get the streets and houses.
+            MvcrParser     parser   = new MvcrParser();
+            ParentResolver resolver = new ParentResolver(location);
+
+            parser.setFilter(resolver.parentRegion == null ? null : resolver.parentRegion.getName(),
+                             resolver.parentViToCi == null ? null : resolver.parentViToCi.getName(),
+                             resolver.parentSuburb == null ? null : resolver.parentSuburb.getName(),
+                             resolver.parentStreet == null ? null : resolver.parentStreet.getName());
+
+            parser.setTargetDatabase(mainDatabase);
+            parser.setStorageDir(pluginDir);
+            try {
+                parser.fillDatabase();
+            } catch (DatabaseLoadException dle) {
+               dle.printStackTrace();
+               System.err.println("CzechAddress: " +
+                            "Selhalo načtení databáze. A teď se budou dít divy...");
+            }*/
+
+            broadcastStatusChanged(StatusListener.MESSAGE_LOCATION_CHANGED);
+            initReasoner();
+        }
+    }
+
+
+    
+    static private List<StatusListener> listeners =
+                    new ArrayList<StatusListener>();
+
+    static public void addStatusListener(StatusListener l) {
+        if (!listeners.contains(l))
+            listeners.add(l);
+    }
+
+    static public void removeStatusListener(StatusListener l) {
+        listeners.remove(l);
+    }
+
+    static public void broadcastStatusChanged(int statusMessage) {
+        for (StatusListener l : listeners)
+            l.pluginStatusChanged(statusMessage);
+    }
+
+    public void pluginStatusChanged(int message) {
+        if (message == StatusListener.MESSAGE_DATABASE_LOADED) {
+            czechMenu = Main.main.menu.addMenu("Adresy", KeyEvent.VK_A, 4);
+            menuItems.add(MainMenu.add(czechMenu, new PointManipulatorAction()));
+            menuItems.add(MainMenu.add(czechMenu, new GroupManipulatorAction()));
+            menuItems.add(MainMenu.add(czechMenu, new ConflictResolveAction()));
+            return;
+        }
+
+        // SelectionMonitor cannot be used because of synchronization problems.
+        /*if (message == MESSAGE_REASONER_REASONED) {
+            System.out.println("ReasonerReasoned");
+            if (selectionMonitor == null)
+                selectionMonitor = new SelectionMonitor();
+        }*/
+    }
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/DatabaseLoadException.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/DatabaseLoadException.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/DatabaseLoadException.java	(revision 15166)
@@ -0,0 +1,26 @@
+package org.openstreetmap.josm.plugins.czechaddress;
+
+import org.openstreetmap.josm.plugins.czechaddress.addressdatabase.Database;
+import org.openstreetmap.josm.plugins.czechaddress.parser.DatabaseParser;
+
+/**
+ * Exception occuring during parsing the database.
+ * 
+ * <p>This exception is used during <i>download</i>, <i>extraction</i> or
+ * <i>parsing</i> of the database. It can set a message to be displayed
+ * to the user by front-end components.</p>
+ *
+ * @author Radomír Černcoh radomir.cernoch@gmail.com
+ * @see Database
+ * @see DatabaseParser
+ */
+public class DatabaseLoadException extends Exception {
+
+    /**
+     * Default constructor, which sets the message description.
+     * @param message message to be shown to user
+     */
+    public DatabaseLoadException(String message) {
+        super(message);
+    }
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/MapUtils.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/MapUtils.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/MapUtils.java	(revision 15166)
@@ -0,0 +1,81 @@
+package org.openstreetmap.josm.plugins.czechaddress;
+
+import java.util.Collection;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
+import org.openstreetmap.josm.gui.MapView;
+
+/**
+ * Collection of utilities for manipulating the JOSM map.
+ *
+ * <p>This set of state-less utilities, which can be handy in all parts of
+ * the plugin. Therefore all methods are {@code static} and the class is
+ * {@code abstract}.</p>
+ *
+ * @author Radomír Černoch radomir.cernoch@gmail.com
+ */
+public abstract class MapUtils {
+
+    static final double NODE_ZOOM_LEVEL = 0.00000007;
+
+
+    /**
+     * Selects and zooms the JOSM viewport to given primitives.
+     *
+     * <p>It does so by calculating the center of given primitives and
+     * then it zooms to it.</p>
+     *
+     * <p><b>WARNING and TODO:</b> The method {@code zoomTo()} currently
+     * checks for damaged {@link Node}s, whose {@code eastNorth} is set to
+     * null. This property is not accessed in this method and therefore
+     * no checking is done. However the "mad GUI" problem may still arise.
+     * Therefore please be careful.</p>
+     *
+     * @see BoundingXYVisitor
+     * @see MapView
+     */
+    public static void zoomToMany(Collection<OsmPrimitive> primitives) {
+        BoundingXYVisitor visitor = new BoundingXYVisitor();
+        for (OsmPrimitive op : primitives) {
+            if (op instanceof Node)
+                ((Node) op).visit(visitor);
+
+            else if (op instanceof Way)
+                ((Way) op).visit(visitor);
+        }
+        Main.map.mapView.zoomTo(
+                visitor.min.interpolate(visitor.max, 0.5),
+                NODE_ZOOM_LEVEL);
+        Main.ds.setSelected(primitives);
+    }
+
+    /**
+     * Selects and zooms the JOSM viewport to given primitive.
+     *
+     * <p><b>TODO:</b> There is an error in JOSM, which makes the whole
+     * GUI totally mad if we zoom to a {@link Node}, whose {@code eastNorth}
+     * is set null. Currently zooming to such a node is ignored, but the
+     * question is where so such damaged nodes come from?</p>
+     *
+     * @see BoundingXYVisitor
+     * @see MapView
+     */
+    public static void zoomTo(OsmPrimitive primitive) {
+        BoundingXYVisitor visitor = new BoundingXYVisitor();
+
+        if (primitive instanceof Node && ((Node) primitive).eastNorth != null)
+            Main.map.mapView.zoomTo(((Node) primitive).eastNorth, NODE_ZOOM_LEVEL);
+
+        else if (primitive instanceof Way) {
+            ((Way) primitive).visit(visitor);
+            Main.map.mapView.zoomTo(
+                    visitor.min.interpolate(visitor.max, 0.5),
+                    NODE_ZOOM_LEVEL);
+        }
+
+        Main.ds.setSelected(primitive);
+    }
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/NotNullList.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/NotNullList.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/NotNullList.java	(revision 15166)
@@ -0,0 +1,46 @@
+package org.openstreetmap.josm.plugins.czechaddress;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * ArrayList, which refuses to add {@code null}.
+ * 
+ * @author Radomír Černoch, radomir.cernoch@gmail.com
+ */
+public class NotNullList<E> extends ArrayList<E> {
+
+    public NotNullList() {
+        super();
+    }
+
+    public NotNullList(int initialCapacity) {
+        super(initialCapacity);
+    }
+
+    /**
+     * If {@code e} is {@code null}, nothing is done. Otherwise like
+     * {@code ArrayList}.
+     */
+    @Override
+    public boolean add(E e) {
+        if (e != null)
+            return super.add(e);
+        else
+            return false;
+    }
+
+    @Override
+    public boolean addAll(Collection<? extends E> c) {
+
+        if (c == null)
+            return false;
+        
+        if (c instanceof NotNullList)
+            return super.addAll(c);
+
+        for (E e : c) add(e);
+        return true;
+    }
+    
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/StatusListener.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/StatusListener.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/StatusListener.java	(revision 15166)
@@ -0,0 +1,103 @@
+package org.openstreetmap.josm.plugins.czechaddress;
+
+import org.openstreetmap.josm.plugins.czechaddress.addressdatabase.AddressElement;
+import org.openstreetmap.josm.plugins.czechaddress.addressdatabase.Database;
+import org.openstreetmap.josm.plugins.czechaddress.parser.DatabaseParser;
+import org.openstreetmap.josm.plugins.czechaddress.intelligence.Reasoner;
+
+/**
+ * Listener capable of sensing plugin status.
+ *
+ * <p>There are several types of events in the plugin, which can happen during
+ * its lifetime. This interface is intended for classes that want to be
+ * aware of such changes.</p>
+ *
+ * <p>Currently there is one single class capable of broadcasting messages to
+ * {@link StatusListener}s, which is the {@link CzechAddressPlugin}. When anyone
+ * wants to notify all listeners about a change in the plugin's status, the
+ * {@link CzechAddressPlugin}{@code .broadcastStatusChanged()} method.</p>
+ *
+ * <p>For registering to the global broadcasting system, use
+ * {@link CzechAddressPlugin}{@code .addStatusListener()} or
+ * {@code removeStatusListener()}.
+ *
+ * @see CzechAddressPlugin
+ * 
+ * @author Radomír Černoch, radomir.cernoch@gmail.com
+ */
+public interface StatusListener {
+
+    /**
+     * The user's choice of location in the database has changed.
+     *
+     * <p>The new location can be obtained by
+     * {@link CzechAddressPlugin}{@code .getLocation()}.</p>
+     *
+     * @see AddressElement
+     */
+    static final int MESSAGE_LOCATION_CHANGED  = 1;
+
+    /**
+     * List of {@code matches} from the reasoner has changed.
+     *
+     * <p>The current reasoner can be obtained by
+     * {@link CzechAddressPlugin}{@code .getReasoner()}.</p>
+     *
+     * @see Reasoner
+     * @see Match
+     */
+    static final int MESSAGE_MATCHES_CHANGED = 2;
+
+    /**
+     * There is a new conflict spotted by the reasoner.
+     *
+     * <p>The current reasoner can be obtained by
+     * {@link CzechAddressPlugin}{@code .getReasoner()}.</p>
+     *
+     * @see Reasoner
+     * @see Match
+     */
+    static final int MESSAGE_CONFLICT_CHANGED  = 3;
+
+    /**
+     * All parsers have finished their parsing.
+     *
+     * <p>The current database can be obtained by
+     * {@link CzechAddressPlugin}{@code .getDatabase()}.</p>
+     *
+     * @see CzechAddressPlugin
+     * @see DatabaseParser
+     * @see Database
+     */
+    static final int MESSAGE_DATABASE_LOADED   = 4;
+
+    /**
+     * The reasoner has finished his reasoning.
+     *
+     * <p>First such message also signalizes, that there is a reasoner
+     * available.</p>
+     *
+     * <p>The current reasoner can be obtained by
+     * {@link CzechAddressPlugin}{@code .getReasoner()}.</p>
+     *
+     * @see Reasoner
+     */
+     static final int MESSAGE_REASONER_REASONED = 5;
+
+    /**
+     * Called when status of the plugin has changed.
+     *
+     * <p>This method is called whenever someone calls
+     * {@link CzechAddressPlugin}{@code .broadcastStatusChanged()}.</p>
+     *
+     * <p>If you are implementing this interface and this method is not
+     * being called, make sure you registered to the messaging system by
+     * {@link CzechAddressPlugin}{@code .addStatusListener()}.</p>
+     *
+     * <p>Parameter {@code message} takes values from {@code MESSAGE_*} fields
+     * of this class.
+     *
+     * @param message the unique ID of the message
+     */
+    void pluginStatusChanged(int message);
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/StringUtils.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/StringUtils.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/StringUtils.java	(revision 15166)
@@ -0,0 +1,32 @@
+package org.openstreetmap.josm.plugins.czechaddress;
+
+/**
+ * Collection of utilities for manipulating strings.
+ *
+ * <p>This set of state-less utilities, which can be handy in all parts of
+ * the plugin. Therefore all methods are {@code static} and the class is
+ * {@code abstract}.</p>
+ *
+ * @author Radomír Černoch, radomir.cernoch@gmail.com
+ */
+public class StringUtils {
+
+    /**
+     * Returns a substring equivalent to <tt>^[0-9]*</tt> regexp match.
+     *
+     * @param s the input string
+     * @return <tt>^[0-9]*</tt> substring match
+     */
+    public static String extractNumber(String s) {
+        String result = "";
+        for (int i=0; i<s.length(); i++) {
+            char ch = s.charAt(i);
+            if (ch >= '0' && ch <= '9')
+                result += ch;
+            else
+                break;
+        }
+        return result;
+    }
+
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/actions/ConflictResolveAction.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/actions/ConflictResolveAction.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/actions/ConflictResolveAction.java	(revision 15166)
@@ -0,0 +1,44 @@
+package org.openstreetmap.josm.plugins.czechaddress.actions;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.plugins.czechaddress.CzechAddressPlugin;
+import org.openstreetmap.josm.plugins.czechaddress.gui.ConflictResolver;
+import org.openstreetmap.josm.tools.Shortcut;
+
+/**
+ * Action, which shows the dialog for resolving conflicts.
+ *
+ * @see ConflictResolver
+ *
+ * @author Radomír Černoch, radomir.cernoch@gmail.com
+ */
+public class ConflictResolveAction extends JosmAction {
+
+    /**
+     * Default constructor, which sets the title, shortcut, ...
+     */
+    public ConflictResolveAction() {
+        super("Zobrazit konflikty",
+              "envelope-closed-exclamation-big.png",
+              "Zobrazí okno s konflikty, které vznikly během přiřazování " +
+                  "objektů mapy k objektům v databázi.",
+              Shortcut.registerShortcut("address:resolveconflict",
+                        "Adresy: Zobrazit konflikty",
+                        KeyEvent.VK_R, Shortcut.GROUP_NONE, Shortcut.SHIFT_DEFAULT),
+              true);
+    }
+
+    /**
+     * If the {@link ConflictResolver} window is not visible, it makes so.
+     *
+     * <p><b>NOTE:</b> There should be only a single such window in the
+     * whole JOSM. Therefore the reference to the unique window is obtained
+     * from {@link CzechAddressPlugin}{@code .conflictResolver}.
+     */
+    public void actionPerformed(ActionEvent e) {
+        if (CzechAddressPlugin.conflictResolver == null) return;
+        CzechAddressPlugin.conflictResolver.setVisible(true);
+    }
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/actions/FactoryAction.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/actions/FactoryAction.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/actions/FactoryAction.java	(revision 15166)
@@ -0,0 +1,111 @@
+package org.openstreetmap.josm.plugins.czechaddress.actions;
+
+import org.openstreetmap.josm.plugins.czechaddress.gui.FactoryDialog;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.mapmode.MapMode;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.plugins.czechaddress.CzechAddressPlugin;
+import org.openstreetmap.josm.plugins.czechaddress.addressdatabase.House;
+import org.openstreetmap.josm.plugins.czechaddress.intelligence.Reasoner;
+import org.openstreetmap.josm.plugins.czechaddress.proposal.ProposalContainer;
+import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.Shortcut;
+
+/**
+ * Mapmode, which creates address elements with "one click".
+ *
+ * <p>This action takes the currently selected {@link House} from the
+ * {@link FactoryDialog} and creates a node for, whenever user clicks
+ * on the map.</p>
+ *
+ * @see FactoryDialog
+ *
+ * @author Radomír Černoch, radomir.cernoch@gmail.com
+ */
+public class FactoryAction extends MapMode {
+
+    /**
+     * Default constructor, which sets the title, shortcut, ...
+     */
+    public FactoryAction(MapFrame frame) {
+        super("Sputit továrnu na adresy",
+              "envelope-cursor.png",
+              "Vytváří adresní body jedním kliknutím",
+              Shortcut.registerShortcut( "mapmode:clickaddress",
+                  "Sputit továrnu na adresy",
+                  KeyEvent.VK_F, Shortcut.GROUP_EDIT),
+              frame,
+              ImageProvider.getCursor("crosshair", "envelope-star-small"));
+    }
+
+    /**
+     * Method called from JOSM when the user selects this mapmode.
+     *
+     * <p>It registers itself into the {@link MapFrame}'s list of
+     * {@link MouseListener}s.</p>
+     */
+    @Override
+    public void enterMode() {
+        super.enterMode();
+        Main.map.mapView.addMouseListener(this);
+
+        Reasoner r = CzechAddressPlugin.getReasoner();
+
+        if (r == null) {
+            exitMode();
+            return;
+        }
+
+        FactoryDialog d = FactoryDialog.getInstance();
+        if (r.translate(d.getSelectedHouse()) != null)
+            d.selectNextUnmatchedHouseByCheckBox();
+    }
+
+    /**
+     * Method called from JOSM when the user deselects this mapmode.
+     *
+     * <p>It unregisters itself from the {@link MapFrame}'s list of
+     * {@link MouseListener}s.</p>
+     */
+    @Override
+    public void exitMode() {
+        Main.map.mapView.removeMouseListener(this);
+        super.exitMode();
+    }
+
+    /**
+     * Core methos of this action, which actually creates the nodes.
+     *
+     * @param e event encapsulating the click-position on the {@link MapFrame}.
+     */
+    @Override
+    public void mouseClicked(MouseEvent e) {
+        super.mouseClicked(e);
+
+        // Get the currently selected House in the FactoryDialog.
+        House house = FactoryDialog.getInstance().getSelectedHouse();
+        if (house == null)
+            return; // TODO: Some meaningful messageBox would be useful.
+
+        // Create a new Node and add it into the map.
+        Node newNode = new Node(Main.map.mapView.getLatLon(e.getX(), e.getY()));
+
+        ProposalContainer container = new ProposalContainer(newNode);
+        container.setProposals(house.getDiff(newNode));
+        container.applyAll();
+
+        // Notify reasoner about this newly added node.
+        CzechAddressPlugin.getReasoner().overwriteMatch(house, newNode);
+
+        // And make the new node selected.
+        Main.ds.addPrimitive(newNode);
+        Main.ds.setSelected(newNode);
+
+        // And do not forget to select next House in the list.
+        FactoryDialog.getInstance().selectNextUnmatchedHouseByCheckBox();
+    }
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/actions/GroupManipulatorAction.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/actions/GroupManipulatorAction.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/actions/GroupManipulatorAction.java	(revision 15166)
@@ -0,0 +1,45 @@
+package org.openstreetmap.josm.plugins.czechaddress.actions;
+
+import org.openstreetmap.josm.plugins.czechaddress.gui.GroupManipulatorDialog;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.plugins.czechaddress.CzechAddressPlugin;
+import org.openstreetmap.josm.tools.Shortcut;
+
+/**
+ * Action adding a menu item for doing address completion.
+ *
+ * @see GroupManipulatorDialog
+ *
+ * @author Radomír Černoch radomir.cernoch@gmail.com
+ */
+public class GroupManipulatorAction extends JosmAction {
+
+    /**
+     * Default constructor, which sets the title, shortcut, ...
+     */
+    public GroupManipulatorAction() {
+        super("Přiřadit adresy",
+              "envelope-closed-big.png",
+              "Přiřadit adresy v celé stáhnuté oblasti",
+              Shortcut.registerShortcut("address:assignaddress",
+                        "Adresy: Přiřadit adresy",
+                        KeyEvent.VK_P, Shortcut.GROUP_DIRECT, Shortcut.SHIFT_DEFAULT),
+              true);
+    }
+
+    /**
+     * Makes the {@link GroupManipulatorDialog} dialog visible.
+     *
+     * <p><b>NOTE:</b> This dialog assumes that the location has already been
+     * selected by the user. Therefore this action checks if the location is not
+     * {@code null}. If so, nothing happens.</p>
+     */
+    public void actionPerformed(ActionEvent e) {
+        if (CzechAddressPlugin.getLocation() == null) return;
+        GroupManipulatorDialog.getInstence().setVisible(true);
+    }
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/actions/PointManipulatorAction.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/actions/PointManipulatorAction.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/actions/PointManipulatorAction.java	(revision 15166)
@@ -0,0 +1,54 @@
+package org.openstreetmap.josm.plugins.czechaddress.actions;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.util.Collection;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.plugins.czechaddress.CzechAddressPlugin;
+import org.openstreetmap.josm.plugins.czechaddress.gui.PointManipulatorDialog;
+import org.openstreetmap.josm.tools.Shortcut;
+
+/**
+ * Action adding a menu item for editing address points
+ *
+ * @see PointManipulatorDialog
+ *
+ * @author Radomír Černoch radomir.cernoch@gmail.com
+ */
+public class PointManipulatorAction extends JosmAction {
+
+    /**
+     * Default constructor, which sets the title, shortcut, ...
+     */
+    public PointManipulatorAction() {
+        super("Vytvořit/upravit adresu",
+              "envelope-open-star-big.png",
+              "Vytvoří nebo upraví adresní bod z čísla popisného.",
+                Shortcut.registerShortcut("tools:newaddress",
+                        "Adresy: Vytvořit/upravit adres",
+                        KeyEvent.VK_A, Shortcut.GROUP_DIRECT, Shortcut.SHIFT_DEFAULT),
+                true);
+    }
+
+    /**
+     * Checks precoditions and eventually shows the dialog.
+     *
+     * <p>This method checks, whether exactly one {@link OsmPrimitive} has been
+     * selected and whether the location was selected
+     * (the {@link PointManipulatorDialog} assumes a location is already
+     * selected. If both conditions are satisfied, dialog is opened.</p>
+     */
+    public void actionPerformed(ActionEvent e) {
+        Collection<OsmPrimitive> data = Main.ds.getSelected();
+
+        if (data.size() != 1) return;
+        OsmPrimitive primitive = (OsmPrimitive) data.toArray()[0];
+
+        if (CzechAddressPlugin.getLocation() == null) return;
+
+        PointManipulatorDialog dialog = new PointManipulatorDialog(primitive);
+        dialog.setVisible(true);
+    }
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/actions/SplitAreaByEmptyWayAction.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/actions/SplitAreaByEmptyWayAction.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/actions/SplitAreaByEmptyWayAction.java	(revision 15166)
@@ -0,0 +1,164 @@
+package org.openstreetmap.josm.plugins.czechaddress.actions;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.util.Collection;
+import java.util.Collections;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.RelationMember;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.tools.Shortcut;
+
+/**
+ * Splits all selected areas (= closed {@link Way}s) into separate areas.
+ * Their border line is any Way, which has no key-value pairs and which
+ * shares first and last node with the splitted area.
+ *
+ * WARNING: The current implementation does not handle relations. If the
+ * original area is a member of some relation, this action rejects to
+ * preform the split.
+ * 
+ * @author Radomír Černoch, radomir.cernoch@gmail.com
+ */
+public class SplitAreaByEmptyWayAction extends JosmAction {
+
+    /**
+     * Defaults constructor, which registers itself into the JOSM menu.
+     */
+    public SplitAreaByEmptyWayAction() {
+        super(tr("Split area"),
+              "splitarea.png",
+              tr("Splits an area by an untagged way."),
+              Shortcut.registerShortcut("tools:splitarea",
+                    tr("Tool: {0}", tr("Split area")),
+                    KeyEvent.VK_S, Shortcut.GROUP_MENU),
+              true);
+    }
+
+    /**
+     * Goes over all selected areas (=closed Ways) and splits them into 2
+     * by any untagged way, which starts and ends at the border line of that
+     * area.
+     */
+    public void actionPerformed(ActionEvent e) {
+		
+        Collection<OsmPrimitive> selectedWays = Main.ds.getSelectedWays();
+        Collection<OsmPrimitive> newSelection = Main.ds.getSelected();
+
+        for (OsmPrimitive prim : selectedWays) {
+            if (!((Way) prim).isClosed()) continue;
+                Way area = (Way) prim;
+        		
+            for (OsmPrimitive prim2 : Main.ds.allNonDeletedPrimitives()) {
+                if (!(prim2 instanceof Way)) continue;
+                if (prim2.equals(prim))      continue;
+               	Way border = (Way) prim2;                		
+                if (border.nodes.size() == 0)   continue;
+                if (border.keySet().size() > 0) continue;
+                if (!area.nodes.contains(border.firstNode())) continue;
+                if (!area.nodes.contains(border.lastNode()))  continue;
+
+                Way newArea1 = new Way();
+                Way newArea2 = new Way();
+	                		
+                int errorCode = splitArea(area, border, newArea1, newArea2);
+	                		
+                if (errorCode == 2) {
+                    JOptionPane.showMessageDialog(Main.parent,
+                        tr("The selected area cannot be splitted, because it is a member of some relation.\n"+
+                            "Remove the area from the relation before splitting it."));
+                    break;
+                }
+	                		
+                if (errorCode == 0) {
+                    Main.ds.addPrimitive(newArea1);
+                    Main.ds.addPrimitive(newArea2);
+	                        
+                    area.delete(true);
+                    border.delete(true);
+                    newSelection.remove(area);
+                    newSelection.remove(border);
+	                		
+                    newSelection.add(newArea1);
+                    newSelection.add(newArea2);
+
+                    break;
+                }
+            }
+        }
+        
+        Main.ds.setSelected(newSelection);
+    }
+
+    /**
+     * Splits a given area into 2 areas. newArea1 and newArea2 must be
+     * referneces to already existing areas.
+     * 
+     * @param area the original area
+     * @param border border line, which goes across the area
+     * @param newArea1 reference to the first new area
+     * @param newArea2 reference to the second new area
+     * @return
+     */
+    private int splitArea(Way area, Way border, Way newArea1, Way newArea2) {
+		
+        Way tempBorder = new Way(border);
+		
+        int index1 = area.nodes.indexOf(tempBorder.firstNode());
+        int index2 = area.nodes.indexOf(tempBorder.lastNode());
+        if (index1 == index2)
+            return 1;
+
+        if (index1 > index2) {
+            Collections.reverse(tempBorder.nodes);
+            index1 = area.nodes.indexOf(tempBorder.firstNode());
+            index2 = area.nodes.indexOf(tempBorder.lastNode());
+        }
+		
+        for (Relation relation : Main.ds.relations)
+            for (RelationMember areaMember : relation.members)
+                if (area.equals(areaMember.member))
+                    return 2;
+			
+        for (String key : area.keySet()) {
+            newArea1.put(key, area.get(key));
+            newArea2.put(key, area.get(key));
+        }
+		
+        newArea1.nodes.addAll(area.nodes.subList(0, index1));
+        newArea1.nodes.addAll(tempBorder.nodes);
+        newArea1.nodes.addAll(area.nodes.subList(index2, area.nodes.size()-1));
+        newArea1.nodes.add(area.nodes.get(0));
+
+        Collections.reverse(tempBorder.nodes);
+        newArea2.nodes.addAll(area.nodes.subList(index1, index2));
+        newArea2.nodes.addAll(tempBorder.nodes);
+        newArea2.nodes.add(area.nodes.get(index1));
+		
+        removeDuplicateNodesFromWay(newArea1);
+        removeDuplicateNodesFromWay(newArea2);
+
+        return 0;
+    }
+	
+    /**
+     * Removes all consequent nodes, which are on the same way.
+     */
+    void removeDuplicateNodesFromWay(Way w) {
+        int i=0;
+        while (i<w.nodes.size()-1) {
+            if (w.nodes.get(i).equals(w.nodes.get(i+1)))
+                w.nodes.remove(i);
+            else
+                i++;
+        }
+    }
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/addressdatabase/AddressElement.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/addressdatabase/AddressElement.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/addressdatabase/AddressElement.java	(revision 15166)
@@ -0,0 +1,278 @@
+package org.openstreetmap.josm.plugins.czechaddress.addressdatabase;
+
+import java.util.List;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.plugins.czechaddress.intelligence.Match;
+import org.openstreetmap.josm.plugins.czechaddress.proposal.Proposal;
+
+/**
+ * The most general element of the houses and streets database.
+ *
+ * <p>Every element must hav a name and may have a parent element.</p>
+ * 
+ * @author Radomír Černoch radomir.cernoch@gmail.com
+ */
+public abstract class AddressElement {
+
+    protected String name;
+    protected AddressElement parent = null;
+
+    protected static final String KEY_ADDR_CP      = "addr:alternatenumber";
+    protected static final String KEY_ADDR_CO      = "addr:housenumber";
+    protected static final String KEY_ADDR_STREET  = "addr:street";
+    protected static final String KEY_ADDR_CITY    = "addr:city";
+    protected static final String KEY_ADDR_COUNTRY = "addr:country";
+    protected static final String KEY_IS_IN        = "is_in";
+    protected static final String KEY_NAME         = "name";
+
+    /**
+     * Constructor setting the name of this element.
+     *
+     * <p><b>TODO:</b> The name is capitalized during the assignment,
+     * because the database contains all names IN CAPITALS. However
+     * this should not be done here, but in the {@link DatabaseParser}.</p>
+     */
+    public AddressElement (String name) {
+        if (name == null)
+            throw new NullPointerException("You must specify the name of this AddressElement");
+        this.name = capitalize(name);
+    }
+
+    /**
+     * Returns the name of this element.
+     *
+     * <p>The name is returned "as it was entered" in the constructor.</p>
+     */
+    public String getName() {
+        return name;
+    }
+
+
+    public static String getName(Object o) {
+        if (o instanceof OsmPrimitive)
+            return getName((OsmPrimitive) o);
+
+        if (o instanceof AddressElement)
+            return ((AddressElement) o).getName();
+
+        return null;
+    }
+
+    public static String getName(OsmPrimitive prim) {
+        String cp     = prim.get("addr:alternatenumber");
+        String co     = prim.get("addr:housenumber");
+        String street = prim.get("addr:street");
+        String city   = prim.get("addr:city");
+        String name   = prim.get("name");
+
+        String result = "";
+
+        // Firstly we try to construct the name from name + place.
+        if (name != null)
+            result += name;
+
+        // We prefer to display street. If not present, try city.
+        if (street != null) {
+            if (result.length() > 0) result += ", ";
+            result += street;
+        } else
+            if (city != null) {
+                if (result.length() > 0) result += ", ";
+                result += city;
+            }
+
+        // If we can display CP, do it!
+        if (cp != null) {
+            if (result.length() > 0) result += " ";
+            result += cp;
+        }
+
+        // If we can display CO, do it!
+        if (co != null) {
+            if (co != null) result += "/";
+            result += co;
+        }
+
+        if (prim instanceof Node)
+            result += " " + StringUtils.latLonToString(((Node) prim).coor);
+        else if (prim instanceof Way)
+            result += " " + StringUtils.latLonToString(((Way) prim).firstNode().coor);
+
+        return result;
+    }
+
+    /**
+     * Sets the new parent of this element.
+     *
+     * <p><b>NOTE:</b> Parent is NOT notified that this element has been
+     * assigned to him. This can be useful for some "ugly hacks".</p>
+     */
+    public void setParent(AddressElement parent) {
+        this.parent = parent;
+    }
+
+    /**
+     * Returns the parent of this element.
+     *
+     * @return the parent element or {@code null} if no parent is present.
+     */
+    public AddressElement getParent() {
+        return parent;
+    }
+
+    /**
+     * Compares 2 elements.
+     * 
+     * <p>Basic criterion to comparing elements is their <i>name</i> and the
+     * </i>parent</i>. Notice that this behaviour might be changed
+     * in subclasses.</p>
+     *
+     * @return {@code false} if <i>name</i> does not match, or <i>parent</i>
+     * does not match or {@code null} is given as a parameter.
+     * {@code true} otherwise.
+     */
+    public boolean equals(AddressElement elem) {
+        if (elem == null)
+            return false;
+        else
+            return name.equals(elem.name) && this.parent == elem.parent;
+    }
+
+    /**
+     * Returns the string representation of this element.
+     *
+     * <p>If the <i>parent</i> of the element is {@code null}, then this equals
+     * to the element's <i>name</i>. Otherwise the result is created recursively
+     * as <tt>"name, parent.name"</tt>.</p>
+     */
+    @Override
+    public String toString() {
+        return (parent == null) ? getName() : getName() + ", " + parent.toString();
+    }
+
+
+    /**
+     * Capitalizes the given string (first letter of every word upper-case,
+     * others lower-case). Czech grammar rules are more or less obeyed.
+     *
+     * <p><b>TODO:</b> This should be moved somewhere else.</p>
+     * 
+     * @param s string to be capitalized
+     * @return capitaized string
+     */
+    protected static String capitalize(String s) {
+
+        if (s == null)
+            return s;
+
+        String result = "";
+
+        char last = ' ';
+        for (char ch : s.toCharArray()) {
+            if ((last >= 'a') && (last <= 'ž') ||
+                (last >= 'A') && (last <= 'Ž'))
+                ch = Character.toLowerCase(ch);
+            else
+                ch = Character.toTitleCase(ch);
+
+            last = ch;
+            result = result + ch;
+        }
+
+        String[] noCapitalize = { "Nad", "Pod", "U", "Na" };
+        for (String noc : noCapitalize)
+            result = result.replaceAll(" "+noc+" ", " "+noc.toLowerCase()+" ");
+
+        return result;
+    }
+    
+    /**
+     * Compute differences between two strings.
+     *
+     * <p>Typical usage of this function is during {@code OsmPrimitive} to
+     * {@code AddressElement} matching. This function helps to whether the given
+     * field does match, does not or causes a conflict.</p>
+     *
+     * <p>This method returns {@code 1} if both fields do match,
+     * {@code 0} if they can be matched and {@code -1} if they do not match.</p>
+     *
+     * <ul>
+     * <li>If the {@code elemValue} is {@code null}, the result is {@code 0}.
+     *     The reason for this is that the database is considered untrustworthy
+     *     by default (compared to the map) and therefore a missing field
+     *     in the database can be matched with an element from the map.</li>
+     *
+     * <li>If the {@code primValue} is {@code null}, but {@code elemValue} is
+     *     not, the map should be changeded. If the database already contains
+     *     a value, we trust it's right. Therefore {@code -1} is returned.</li>
+     *
+     * <li>When both fields are not {@code null}, they are compared by their
+     *     upper-case trimmed value. {@code 1} is equals, {@code -1}
+     *     otherwise.</li>
+     * </ul>
+     *
+     * @param elemValue value of the {@link AddressElement}'s field
+     * @param primValue value of the {@link OsmPrimitive}'s field
+     *
+     * @return {@code -1}, {@code 0} or {@code 1} (see above)
+     */
+    public static int matchField(String elemValue, String primValue) {
+
+        if (elemValue == null) return  0;
+        if (primValue == null) return -1;
+
+        return (primValue.trim().toUpperCase().equals(
+                elemValue.trim().toUpperCase())) ? 1 : -1;
+    }
+    
+    
+    protected int[] getFieldMatchList(OsmPrimitive primitive) {
+        int[] result = {0};        
+        return result;
+    }
+
+
+    public List<Proposal> getDiff(OsmPrimitive prim) {
+        return null;
+    }
+
+    
+    public int getMatchQuality(OsmPrimitive primitive) {
+        
+        // Firstly get integers representing a match of every matchable field.
+        int[] fieldMatches = getFieldMatchList(primitive);
+        assert fieldMatches.length > 0;
+        
+        // Now find the max and min of this array.
+        int minVal = fieldMatches[0];
+        int maxVal = fieldMatches[0];
+        for (int i=1; i<fieldMatches.length; i++) {
+            if (minVal > fieldMatches[i])
+                minVal = fieldMatches[i];
+            if (maxVal < fieldMatches[i])
+                maxVal = fieldMatches[i];
+        }
+
+        // Check valid results
+        assert Math.abs(minVal) <= 1;
+        assert Math.abs(maxVal) <= 1;
+        
+        // If the best among all fields is 'neutral' match, the given primitive
+        // has nothing to do with our field.
+        if (maxVal <= 0)
+            return Match.MATCH_NOMATCH;
+        
+        // If all fields are 1    --> ROCKSOLID MATCH
+        // If some are 1, none -1 --> PARTIAL MATCH
+        // If some are 1, some -1 --> CONFLICT
+        switch (minVal * maxVal) {
+            case -1 : return Match.MATCH_CONFLICT;
+            case  0 : return Match.MATCH_PARTIAL;
+            case +1 : return Match.MATCH_ROCKSOLID;   
+        }
+        
+        return 0; // <-- just to make compilers happy. We cannot get here.
+    }    
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/addressdatabase/Database.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/addressdatabase/Database.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/addressdatabase/Database.java	(revision 15166)
@@ -0,0 +1,48 @@
+package org.openstreetmap.josm.plugins.czechaddress.addressdatabase;
+
+import java.util.ArrayList;
+
+/**
+ * Stores the whole database of all regions, municipalities, suburbs, streets
+ * and houses in the Czech republic. The database can be feeded via
+ * XML and is capable of downloading it from www.mvcr.cz
+ * 
+ * @see AddressElement
+ * @see DatabaseParser
+
+ * @author Radomir Cernoch radomir.cernoch@gmail.com
+ */
+public class Database {
+
+    /**
+     * List of regions, which are in this database.
+     */
+    public ArrayList<Region> regions = new ArrayList<Region>();
+
+    public Region findRegion(String name, String nuts3, String nuts4) {
+
+        if (name == null) return null;
+
+        name = name.toUpperCase();
+        if (nuts3 != null) nuts3 = nuts3.toUpperCase();
+        if (nuts4 != null) nuts4 = nuts4.toUpperCase();
+
+        for (Region region : regions) {
+
+            if (!region.getName().toUpperCase().equals(name))
+                continue;
+
+            if ( region.getNuts3Name() != null &&
+                !region.getNuts3Name().toUpperCase().equals(nuts3))
+                continue;
+
+            if ( region.getNuts4Name() != null &&
+                !region.getNuts4Name().toUpperCase().equals(nuts3))
+                continue;
+
+            return region;
+        }
+
+        return null;
+    }
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/addressdatabase/ElementWithHouses.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/addressdatabase/ElementWithHouses.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/addressdatabase/ElementWithHouses.java	(revision 15166)
@@ -0,0 +1,62 @@
+package org.openstreetmap.josm.plugins.czechaddress.addressdatabase;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Element, which consists of a number of houses.
+ *
+ * @author Radomir Cernoch radomir.cernoch@gmail.com
+ */
+abstract public class ElementWithHouses extends AddressElement {
+
+    /**
+     * Default constructor setting the name of this element.
+     */
+    public ElementWithHouses(String name) {
+        super(name);
+    }
+
+    protected List<House> houses = new ArrayList<House>();
+
+    /**
+     * Inserts a new house into this element.
+     */
+    public void addHouse(House houseToAdd) {
+        houses.add(houseToAdd);
+        houseToAdd.setParent(this);
+    }
+
+    /**
+     * Inserts houses into this element.
+     */
+    public void addHouses(List<House> housesToAdd) {
+        for (House houseToAdd : housesToAdd)
+            addHouse(houseToAdd);
+    }
+
+    /**
+     * Replaces the internal list of houses by a new one.
+     */
+    public void setHouses(List<House> houses) {
+        this.houses = houses;
+        for (House house : this.houses)
+            house.setParent(this);
+    }
+
+    /**
+     * Returns all houses directly contained in this element.
+     * 
+     * NOTICE: If a subclass element contains other data structured capable
+     * of storing houses, they are not included in the returned set.
+     *
+     * Eg. {@code ElementWithStreets.getHouses()} returns only such houses, which
+     * do not belong to any street.
+     */
+    public List<House> getHouses() {
+        return houses;
+    }
+
+
+
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/addressdatabase/ElementWithStreets.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/addressdatabase/ElementWithStreets.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/addressdatabase/ElementWithStreets.java	(revision 15166)
@@ -0,0 +1,89 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package org.openstreetmap.josm.plugins.czechaddress.addressdatabase;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Element consisting of a number of streets (apart from houses inherited
+ * from {@link ElementWithHouse}).
+ *
+ * @author Radomir Cernoch radomir.cernoch@gmail.com
+ */
+abstract public class ElementWithStreets extends ElementWithHouses {
+
+    private ArrayList<Street> streets = new ArrayList<Street>();
+
+    public ElementWithStreets(String name) {
+        super(name);
+    }
+
+    /**
+     * Adds a new street into this element.
+     */
+    public void addStreet(Street streetToAdd) {
+        //if (streetToAdd.getParent() instanceof ElementWithStreets)
+        
+        streetToAdd.setParent(this);
+        streets.add(streetToAdd);
+    }
+
+    /**
+     * Adds new streets into this element.
+     */
+    public void addStreets(Collection<Street> streetsToAdd) {
+        streets.ensureCapacity(streets.size() + streetsToAdd.size() );
+        for (Street streetToAdd : streetsToAdd)
+            addStreet(streetToAdd);
+    }
+
+    /**
+     * Replaces the internal list of streets with another one.
+     */
+    public void setStreets(ArrayList<Street> streets) {
+        this.streets = streets;
+        for (Street street : streets) {
+            street.setParent(this);
+        }
+    }
+
+    /**
+     * Returns the list of streets.
+     */
+    public List<Street> getStreets() {
+        return streets;
+    }
+
+    public Street findStreet(String streetName) {
+
+        if (streetName == null) return null;
+
+        streetName = streetName.toUpperCase();
+
+        for (Street street : streets)
+            if (street.getName().toUpperCase().equals(streetName))
+                return street;
+        return null;
+    }
+
+    /**
+     * Returns all houses belonging directly to this element together
+     * with all houses from streets in this element.
+     */
+    public List<House> getAllHouses() {
+        // We make an conservative estimate...
+        List<House> result = new ArrayList<House>(20 * streets.size());
+        
+        result.addAll(this.houses);
+        
+        for (Street street : streets)
+            result.addAll(street.getHouses());
+        
+        return result;
+    }
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/addressdatabase/House.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/addressdatabase/House.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/addressdatabase/House.java	(revision 15166)
@@ -0,0 +1,204 @@
+package org.openstreetmap.josm.plugins.czechaddress.addressdatabase;
+
+import java.util.List;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.plugins.czechaddress.NotNullList;
+import org.openstreetmap.josm.plugins.czechaddress.proposal.Proposal;
+
+import static org.openstreetmap.josm.plugins.czechaddress.proposal.ProposalFactory.getStringFieldDiff;
+import static org.openstreetmap.josm.plugins.czechaddress.proposal.ProposalFactory.getListFieldDiff;
+
+/**
+ * Represents a single house.
+ *
+ * <p> Every house contains a number within the street
+ * (číslo orientační) or the number within the suburb (číslo popisné).
+ * Both numbers are stored as string to allow house numbers like '1a'.</p>
+ *
+ * <p><b>WARNING:</b> Every house must have a parent, whose type is
+ * {@link ElementWithHouses}. The {@code setParent()} method should
+ * be called immediatelly after the constructor.</p>
+ * 
+ * @author Radomír Černoch, radomir.cernoch@gmail.com
+ */
+public class House extends AddressElement {
+
+    /** Stores the number unique in a suburb (číslo popisné). */
+    protected String cp = null;
+    /** Stores the number unique in a street (číslo orientační). */
+    protected String co = null;
+
+    /**
+     * Default constructor setting the numbers of this House.
+     *
+     * @param cp number unique in a suburb (číslo popisné)
+     * @param co number unique in a street (číslo orientační)
+     */
+    public House(String cp, String co) {
+        // Firstly we pretend that this element has no name...
+        super("");
+
+        if (cp != null) this.cp = cp.toLowerCase();
+        if (co != null) this.co = co.toLowerCase();
+
+        assert (co != null) || (cp != null);
+        
+        //... but the the name is overwritten.
+        this.name = generateName(this.cp, this.co);
+    }
+    
+    /**
+     * Returns the number unique in a suburb (číslo popisné)
+     */
+    public String getCP() {
+        return cp;
+    }
+
+    /**
+     * Returns the number unique in a street (číslo orientační)
+     */
+    public String getCO() {
+        return co;
+    }
+
+    /**
+     * Generates a name from house numbers.
+     *
+     * <p><b>WARNING:</b> Only one number can be {@link null}.</p>
+     *
+     * <p>If only one number is given, it is returned directly. If both numbers
+     * are given, they are combined in the way, which is standard in CZ.</p>
+     *
+     * @param cp number unique in a suburb (číslo popisné)
+     * @param co number unique in a street (číslo orientační)
+     * @return string representation of a house with given house numbers.
+     */
+    public static String generateName(String cp, String co) {
+
+        if ((cp != null) && (co != null))
+            return cp + "/" + co;
+
+        if (co != null) return co;
+        if (cp != null) return cp;
+
+        assert false;
+        return ""; // <-- just to make compiler happy.
+    }
+
+    /**
+     * Returns the full name of this house (including street/suburb).
+     *
+     * <p><b>WARNING:</b> Every house must have a parent, whose type is
+     * {@link ElementWithHouses}. The {@code setParent()} method should
+     * be called immediatelly after the constructor.</p>
+     */
+    @Override
+    public String getName() {
+        assert parent != null : "A house must always have a parent.";
+        return parent.getName() + " " + name;
+    }
+
+    /**
+     * Sets the parent of this house with type-checking.
+     *
+     * <p><b>WARNING:</b> Every house must have a parent, whose type is
+     * {@link ElementWithHouses}. The {@code setParent()} method should
+     * be called immediatelly after the constructor.</p>
+     */
+    @Override
+    public void setParent(AddressElement parent) {
+        assert parent instanceof ElementWithHouses;
+        super.setParent(parent);
+    }
+    
+    /**
+     * Returns the parent of this house with type-checking.
+     *
+     * <p><b>WARNING:</b> Every house must have a parent, whose type is
+     * {@link ElementWithHouses}. The {@code setParent()} method should
+     * be called immediatelly after the constructor.</p>
+     */
+    @Override
+    public ElementWithHouses getParent() {
+        assert parent instanceof ElementWithHouses;
+        return (ElementWithHouses) parent;
+    }
+
+    /**
+     * Returns an array describing how the house fits to given primitive.
+     *
+     * <p>Values of the array are described in
+     * {@link AddressElement}{@code .getFieldMatchList()}.</p>
+     *
+     * <p>First elemtn of the returned array corresponds to the CP
+     * (číslo popisné), the second one to the combination of Street+CO.</p>
+     */
+    @Override
+    protected int[] getFieldMatchList(OsmPrimitive prim) {
+        int[] result = {0, 0};
+                
+        // First field is the AlternateNubmer
+        result[0] = matchField(this.cp, prim.get(KEY_ADDR_CP));
+        
+        // Second field is the Housenumber
+        if (parent instanceof Street)
+            result[1] = Math.min( matchField(parent.getName(), prim.get(KEY_ADDR_STREET)),
+                                  matchField(this.co,          prim.get(KEY_ADDR_CO)) );
+        return result;
+    }
+    
+    /**
+     * Gives all proposals to make the primitive be an address primitive.
+     *
+     * <p>Tries to fill {@code prim}'s attributes to have all attributes that
+     * this House has.</p>
+     */
+    @Override
+    public List<Proposal> getDiff(OsmPrimitive prim) {
+        
+        List<Proposal> props = new NotNullList<Proposal>();
+        ParentResolver pr = new ParentResolver(this);
+
+        props.add(getStringFieldDiff(KEY_ADDR_CP, prim.get(KEY_ADDR_CP), getCP()));
+        props.add(getStringFieldDiff(KEY_ADDR_CO, prim.get(KEY_ADDR_CO), getCO()));
+
+        props.add(getStringFieldDiff(KEY_ADDR_COUNTRY,
+                            prim.get(KEY_ADDR_COUNTRY), "CZ"));
+
+        if (pr.parentStreet != null)
+            props.add(getStringFieldDiff(KEY_ADDR_STREET,
+                                prim.get(KEY_ADDR_STREET),
+                                pr.parentStreet.getName()));
+        
+        if (pr.parentViToCi != null) {
+            String targetIsIn = "";
+
+            if ((pr.parentSuburb != null) &&
+                !(pr.parentViToCi.getName().equals(pr.parentSuburb.getName())))
+                targetIsIn += pr.parentSuburb.getName() + ", ";
+
+            targetIsIn += pr.parentViToCi.getName() + ", ";
+
+            if (pr.parentRegion != null)
+                targetIsIn += pr.parentRegion.getNuts3Name() + " kraj, ";
+
+            targetIsIn += "CZ";
+
+            props.add(getStringFieldDiff(KEY_ADDR_CITY,
+                                prim.get(KEY_ADDR_CITY),
+                                pr.parentViToCi.getName()));
+
+            props.add(getStringFieldDiff(KEY_IS_IN,
+                                prim.get(KEY_IS_IN),
+                                targetIsIn));
+        }
+
+        // If we have added any proposal so far, add the source info as well.
+        if (props.size() > 0)
+            props.add(getListFieldDiff("source", prim.get("source:addr"), "mvcr:adresa"));
+
+        return props;
+    }
+
+
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/addressdatabase/ParentResolver.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/addressdatabase/ParentResolver.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/addressdatabase/ParentResolver.java	(revision 15166)
@@ -0,0 +1,41 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package org.openstreetmap.josm.plugins.czechaddress.addressdatabase;
+
+/**
+ *
+ * @author Radomír Černoch, radomir.cernoch@gmail.com
+ */
+public class ParentResolver {
+
+    public Street parentStreet = null;
+    public Suburb parentSuburb = null;
+    public ViToCi parentViToCi = null;
+    public Region parentRegion = null;
+
+    public ParentResolver(AddressElement queryElement) {
+
+        if (queryElement.getParent() instanceof Street) {
+            parentStreet = (Street) queryElement.getParent();
+            queryElement = queryElement.getParent();
+        }
+
+        if (queryElement.getParent() instanceof Suburb) {
+            parentSuburb = (Suburb) queryElement.getParent();
+            queryElement = queryElement.getParent();
+        }
+
+        if (queryElement.getParent() instanceof ViToCi) {
+            parentViToCi = (ViToCi) queryElement.getParent();
+            queryElement = queryElement.getParent();
+        }
+
+        if (queryElement.getParent() instanceof Region) {
+            parentRegion = (Region) queryElement.getParent();
+            queryElement = queryElement.getParent();
+        }
+    }
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/addressdatabase/Region.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/addressdatabase/Region.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/addressdatabase/Region.java	(revision 15166)
@@ -0,0 +1,97 @@
+package org.openstreetmap.josm.plugins.czechaddress.addressdatabase;
+
+import java.util.ArrayList;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+
+/**
+ * Region is an area within the country. It contains {@link ViToCi}s (villages,
+ * towns, cities).
+ *
+ * @author Radomír Černoch radomir.cernoch@gmail.com
+ */
+public class Region extends ElementWithStreets {
+    
+    private ArrayList<ViToCi> vitocis
+            = new ArrayList<ViToCi>();
+    
+    /**
+     * Adds a single municipality into this element.
+     */
+    public void addViToCi(ViToCi municipality) {
+        municipality.setParent(this);
+        vitocis.add(municipality);
+    }
+    
+    /**
+     * Replaces the list of municipalities of this element.
+     */
+    public void setViToCis(ArrayList<ViToCi> municipalities) {
+        this.vitocis = municipalities;
+        for (ViToCi obec : municipalities)
+            obec.setParent(this);
+    }
+    
+    /**
+     * Returns the list of all municipalities in this region.
+     */
+    public ArrayList<ViToCi> getViToCis() {
+        return vitocis;
+    }
+
+    public ViToCi findViToCi(String viToCiName) {
+
+        if (viToCiName == null) return null;
+
+        viToCiName = viToCiName.toUpperCase();
+
+        for (ViToCi vitoci : vitocis)
+            if (vitoci.getName().toUpperCase().equals(name))
+                return vitoci;
+
+        return null;
+    }
+
+    String nuts3name = null;
+    String nuts4name = null;
+    
+    /**
+     * Default constructor setting the name of this region.
+     * @param name
+     */
+    public Region(String name) {
+        super(name);
+    }
+    
+    /**
+     * Constructor which sets the region's name together with higher
+     * administrative areas.
+     */
+    public Region(String name, String nuts3name, String nuts4name) {
+        super(name);
+        if (nuts3name != null) this.nuts3name = capitalize(nuts3name);
+        if (nuts4name != null) this.nuts4name = capitalize(nuts4name);
+    }
+    
+    public String getNuts3Name() {
+        return nuts3name;
+    }
+    
+    public String getNuts4Name() {
+        return nuts4name;
+    }
+    
+    /**
+     * Returns the name of this region. If the NUTS3 name was entered,
+     * its name is appended to the result.
+     */
+    @Override
+    public String toString() {
+        
+        String thisString = name;
+        
+        if (nuts3name != null)
+            thisString += " (kraj " + nuts3name + ")";
+        
+        return thisString;
+    }
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/addressdatabase/Street.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/addressdatabase/Street.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/addressdatabase/Street.java	(revision 15166)
@@ -0,0 +1,38 @@
+package org.openstreetmap.josm.plugins.czechaddress.addressdatabase;
+
+/**
+ * Street is a member of {@link ElementWithStreets}
+ *
+ * @author Radomír Černoch radomir.cernoch@gmail.com
+ */
+public class Street extends ElementWithHouses  {
+
+    public Street(String name) {
+        super(name);
+    }
+
+    @Override
+    public void setParent(AddressElement parent) {
+        assert parent instanceof ElementWithStreets;
+        super.setParent(parent);
+    }
+    
+    @Override
+    public ElementWithStreets getParent() {
+        assert parent instanceof ElementWithStreets;
+        return (ElementWithStreets) parent;
+    }
+
+
+
+    /*int[] getFieldMatchList(OsmPrimitive primitive) {
+        int[] result = {0};
+        
+        if (primitive.get("highway") == null)
+            return result;
+        
+        result[0] = matchField(name, primitive.get("name"));
+        
+        return result;
+    }*/
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/addressdatabase/StringUtils.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/addressdatabase/StringUtils.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/addressdatabase/StringUtils.java	(revision 15166)
@@ -0,0 +1,33 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package org.openstreetmap.josm.plugins.czechaddress.addressdatabase;
+
+import org.openstreetmap.josm.data.coor.LatLon;
+
+/**
+ *
+ * @author radek
+ */
+public class StringUtils {
+
+    public static String coordinateToString(double coor) {
+        double degrees = Math.floor(coor);
+        double minutes = Math.floor( 60*(coor-degrees) );
+        double seconds = 60*60*(coor-degrees-minutes/60);
+        
+        return String.valueOf(Math.round(    degrees))     + "°" +
+               String.valueOf(Math.round(    minutes))     + "'" +
+               String.valueOf(Math.round(100*seconds)/100.0) + "\"";
+    }
+
+    public static String latLonToString(LatLon position) {
+        if (position == null) return "";
+
+        return "(lat: " + coordinateToString(position.lat())
+             + " lon: " + coordinateToString(position.lon()) + ")";
+    }
+
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/addressdatabase/Suburb.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/addressdatabase/Suburb.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/addressdatabase/Suburb.java	(revision 15166)
@@ -0,0 +1,13 @@
+package org.openstreetmap.josm.plugins.czechaddress.addressdatabase;
+
+/**
+ * Suburb is a part of a {@link Municipality}.
+ * 
+ * @author Radomír Černoch radomir.cernoch@gmail.com
+ */
+public class Suburb extends ElementWithStreets {
+
+    public Suburb(String name) {
+        super(name);
+    }
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/addressdatabase/ViToCi.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/addressdatabase/ViToCi.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/addressdatabase/ViToCi.java	(revision 15166)
@@ -0,0 +1,58 @@
+package org.openstreetmap.josm.plugins.czechaddress.addressdatabase;
+
+import java.util.List;
+import java.util.ArrayList;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+
+/**
+ * ViToCi is either a village, town or a city.
+ * 
+ * @author Radomír Černoch radomir.cernoch@gmail.com
+ */
+public class ViToCi extends ElementWithStreets {
+
+    private ArrayList<Suburb> suburbs = new ArrayList<Suburb>();
+
+    /**
+     * Default constructor setting the name of this element.
+     */
+    public ViToCi(String name) {
+        super(name);
+    }
+
+    /**
+     * Adds a new suburb to this municipality.
+     */
+    public void addSuburb(Suburb suburbToAdd) {
+        suburbToAdd.setParent(this);
+        suburbs.add(suburbToAdd);
+    }
+
+    /**
+     * Adds new suburbs to this municipality.
+     */
+    public void addSuburbs(List<Suburb> suburbsToAdd) {
+        for (Suburb suburbToAdd : suburbsToAdd)
+            addSuburb(suburbToAdd);
+    }
+
+    /**
+     * Returns the list of all suburbs of this municipality.
+     */
+    public ArrayList<Suburb> getSuburbs() {
+        return suburbs;
+    }
+
+    public Suburb findSuburb(String suburbName) {
+
+        if (suburbName == null) return null;
+
+        suburbName = suburbName.toUpperCase();
+
+        for (Suburb suburb : suburbs)
+            if (suburb.getName().toUpperCase().equals(suburbName))
+                return suburb;
+
+        return null;
+    }
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/ConflictResolver.form
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/ConflictResolver.form	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/ConflictResolver.form	(revision 15166)
@@ -0,0 +1,201 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<Form version="1.3" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
+  <Properties>
+    <Property name="defaultCloseOperation" type="int" value="2"/>
+  </Properties>
+  <SyntheticProperties>
+    <SyntheticProperty name="formSizePolicy" type="int" value="1"/>
+  </SyntheticProperties>
+  <AuxValues>
+    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+    <AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,36,0,0,2,13"/>
+  </AuxValues>
+
+  <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridLayout">
+    <Property name="columns" type="int" value="0"/>
+    <Property name="rows" type="int" value="1"/>
+  </Layout>
+  <SubComponents>
+    <Container class="javax.swing.JPanel" name="mainPanel">
+
+      <Layout>
+        <DimensionLayout dim="0">
+          <Group type="103" groupAlignment="0" attributes="0">
+              <Group type="102" alignment="1" attributes="0">
+                  <Group type="103" groupAlignment="0" attributes="0">
+                      <Component id="jLabel4" min="-2" max="-2" attributes="0"/>
+                      <Component id="jLabel5" min="-2" max="-2" attributes="0"/>
+                      <Component id="jLabel6" min="-2" max="-2" attributes="0"/>
+                  </Group>
+                  <EmptySpace min="-2" max="-2" attributes="0"/>
+                  <Group type="103" groupAlignment="0" attributes="0">
+                      <Group type="102" alignment="1" attributes="0">
+                          <Component id="candTextField" pref="232" max="32767" attributes="1"/>
+                          <EmptySpace max="-2" attributes="0"/>
+                          <Component id="candReassignButton" min="-2" max="-2" attributes="0"/>
+                      </Group>
+                      <Component id="bestTextField" alignment="0" pref="319" max="32767" attributes="1"/>
+                      <Component id="mainTextField" alignment="0" pref="319" max="32767" attributes="1"/>
+                  </Group>
+                  <EmptySpace min="-2" max="-2" attributes="0"/>
+                  <Group type="103" groupAlignment="1" attributes="0">
+                      <Component id="mainZoomButton" min="-2" max="-2" attributes="0"/>
+                      <Component id="bestZoomButton" min="-2" max="-2" attributes="0"/>
+                      <Component id="candZoomButton" min="-2" max="-2" attributes="0"/>
+                  </Group>
+              </Group>
+              <Component id="jTabbedPane1" alignment="0" pref="525" max="32767" attributes="1"/>
+          </Group>
+        </DimensionLayout>
+        <DimensionLayout dim="1">
+          <Group type="103" groupAlignment="0" attributes="0">
+              <Group type="102" alignment="1" attributes="0">
+                  <Component id="jTabbedPane1" pref="199" max="32767" attributes="0"/>
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Group type="103" groupAlignment="3" attributes="0">
+                      <Component id="mainTextField" alignment="3" min="-2" max="-2" attributes="0"/>
+                      <Component id="mainZoomButton" alignment="3" min="-2" max="-2" attributes="0"/>
+                      <Component id="jLabel4" alignment="3" min="-2" max="-2" attributes="0"/>
+                  </Group>
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Group type="103" groupAlignment="3" attributes="0">
+                      <Component id="bestTextField" alignment="3" min="-2" max="-2" attributes="0"/>
+                      <Component id="bestZoomButton" alignment="3" min="-2" max="-2" attributes="0"/>
+                      <Component id="jLabel5" alignment="3" min="-2" max="-2" attributes="0"/>
+                  </Group>
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Group type="103" groupAlignment="3" attributes="0">
+                      <Component id="candReassignButton" alignment="3" min="-2" max="-2" attributes="0"/>
+                      <Component id="candTextField" alignment="3" min="-2" max="-2" attributes="0"/>
+                      <Component id="candZoomButton" alignment="3" min="-2" max="-2" attributes="0"/>
+                      <Component id="jLabel6" alignment="3" min="-2" max="-2" attributes="0"/>
+                  </Group>
+              </Group>
+          </Group>
+        </DimensionLayout>
+      </Layout>
+      <SubComponents>
+        <Component class="javax.swing.JButton" name="candZoomButton">
+          <Properties>
+            <Property name="text" type="java.lang.String" value="     "/>
+          </Properties>
+          <Events>
+            <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="candZoomButtonActionPerformed"/>
+          </Events>
+        </Component>
+        <Component class="javax.swing.JButton" name="bestZoomButton">
+          <Properties>
+            <Property name="text" type="java.lang.String" value="     "/>
+          </Properties>
+          <Events>
+            <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bestZoomButtonActionPerformed"/>
+          </Events>
+        </Component>
+        <Container class="javax.swing.JTabbedPane" name="jTabbedPane1">
+
+          <Layout class="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout"/>
+          <SubComponents>
+            <Container class="javax.swing.JScrollPane" name="jScrollPane1">
+              <AuxValues>
+                <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
+              </AuxValues>
+              <Constraints>
+                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
+                  <JTabbedPaneConstraints tabName="Dle datab&#xe1;ze">
+                    <Property name="tabTitle" type="java.lang.String" value="Dle datab&#xe1;ze"/>
+                  </JTabbedPaneConstraints>
+                </Constraint>
+              </Constraints>
+
+              <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+              <SubComponents>
+                <Component class="javax.swing.JTree" name="elemConflictTree">
+                  <Events>
+                    <EventHandler event="valueChanged" listener="javax.swing.event.TreeSelectionListener" parameters="javax.swing.event.TreeSelectionEvent" handler="treeValueChanged"/>
+                  </Events>
+                </Component>
+              </SubComponents>
+            </Container>
+            <Container class="javax.swing.JScrollPane" name="jScrollPane2">
+              <AuxValues>
+                <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
+              </AuxValues>
+              <Constraints>
+                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
+                  <JTabbedPaneConstraints tabName="Dle mapy">
+                    <Property name="tabTitle" type="java.lang.String" value="Dle mapy"/>
+                  </JTabbedPaneConstraints>
+                </Constraint>
+              </Constraints>
+
+              <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+              <SubComponents>
+                <Component class="javax.swing.JTree" name="primConflictTree">
+                  <Events>
+                    <EventHandler event="valueChanged" listener="javax.swing.event.TreeSelectionListener" parameters="javax.swing.event.TreeSelectionEvent" handler="treeValueChanged"/>
+                  </Events>
+                </Component>
+              </SubComponents>
+            </Container>
+          </SubComponents>
+        </Container>
+        <Component class="javax.swing.JLabel" name="jLabel4">
+          <Properties>
+            <Property name="text" type="java.lang.String" value="Objekt v konfliktu:"/>
+          </Properties>
+        </Component>
+        <Component class="javax.swing.JTextField" name="mainTextField">
+          <Properties>
+            <Property name="editable" type="boolean" value="false"/>
+          </Properties>
+        </Component>
+        <Component class="javax.swing.JTextField" name="bestTextField">
+          <Properties>
+            <Property name="editable" type="boolean" value="false"/>
+          </Properties>
+        </Component>
+        <Component class="javax.swing.JLabel" name="jLabel5">
+          <Properties>
+            <Property name="text" type="java.lang.String" value="Nejlep&#x161;&#xed; p&#x159;i&#x159;azen&#xed;:"/>
+          </Properties>
+        </Component>
+        <Component class="javax.swing.JTextField" name="candTextField">
+          <Properties>
+            <Property name="editable" type="boolean" value="false"/>
+          </Properties>
+        </Component>
+        <Component class="javax.swing.JButton" name="candReassignButton">
+          <Properties>
+            <Property name="text" type="java.lang.String" value="P&#x159;i&#x159;adit"/>
+            <Property name="enabled" type="boolean" value="false"/>
+          </Properties>
+          <Events>
+            <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="candReassignButtonActionPerformed"/>
+          </Events>
+        </Component>
+        <Component class="javax.swing.JLabel" name="jLabel6">
+          <Properties>
+            <Property name="text" type="java.lang.String" value="Probl&#xe9;mov&#xfd; element:"/>
+          </Properties>
+        </Component>
+        <Component class="javax.swing.JButton" name="mainZoomButton">
+          <Properties>
+            <Property name="text" type="java.lang.String" value="     "/>
+          </Properties>
+          <Events>
+            <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="mainZoomButtonActionPerformed"/>
+          </Events>
+        </Component>
+      </SubComponents>
+    </Container>
+  </SubComponents>
+</Form>
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/ConflictResolver.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/ConflictResolver.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/ConflictResolver.java	(revision 15166)
@@ -0,0 +1,420 @@
+package org.openstreetmap.josm.plugins.czechaddress.gui;
+
+import java.util.List;
+import java.util.logging.Logger;
+import org.openstreetmap.josm.plugins.czechaddress.*;
+import javax.swing.Icon;
+import javax.swing.tree.TreePath;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.gui.ExtendedDialog;
+import org.openstreetmap.josm.plugins.czechaddress.addressdatabase.AddressElement;
+import org.openstreetmap.josm.plugins.czechaddress.intelligence.Match;
+import org.openstreetmap.josm.plugins.czechaddress.intelligence.Reasoner;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+/**
+ * Dialog for displaying and handling conflicts.
+ *
+ * @author Radomír Černoch, radomir.cernoch@gmail.com
+ */
+public class ConflictResolver extends ExtendedDialog
+        implements StatusListener {
+
+    private Object selMainObj = null;
+    private Object selBestFit = null;
+    private Object selCandObj = null;
+
+    /**
+     * Creates new dialog, but does not display it, nor hook to messages.
+     */
+    public ConflictResolver() {
+
+        super(Main.parent, "Řešení konfliktů",
+                       new String[] { }, true);
+
+        initComponents();
+
+        elemConflictTree.setModel(new ElemTreeModel());
+        primConflictTree.setModel(new PrimTreeModel());
+
+        elemConflictTree.setCellRenderer(new UniversalTreeRenderer());
+        primConflictTree.setCellRenderer(new UniversalTreeRenderer());
+
+        // Create those lovely 'zoom' icons for professional look.
+        Icon zoomIcon = ImageProvider.get("zoom.png");
+        mainZoomButton.setIcon(zoomIcon); mainZoomButton.setText("");
+        bestZoomButton.setIcon(zoomIcon); bestZoomButton.setText("");
+        candZoomButton.setIcon(zoomIcon); candZoomButton.setText("");
+
+        // And finalize initializing the form.
+        setupDialog(mainPanel, new String[] { });
+        setAlwaysOnTop(false);
+
+        // TODO: Why does it always crash if the modality is set in constructor?
+        setModal(false);
+    }
+
+    /** This method is called from within the constructor to
+     * initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is
+     * always regenerated by the Form Editor.
+     */
+    @SuppressWarnings("unchecked")
+    // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
+    private void initComponents() {
+        mainPanel = new javax.swing.JPanel();
+        candZoomButton = new javax.swing.JButton();
+        bestZoomButton = new javax.swing.JButton();
+        jTabbedPane1 = new javax.swing.JTabbedPane();
+        jScrollPane1 = new javax.swing.JScrollPane();
+        elemConflictTree = new javax.swing.JTree();
+        jScrollPane2 = new javax.swing.JScrollPane();
+        primConflictTree = new javax.swing.JTree();
+        jLabel4 = new javax.swing.JLabel();
+        mainTextField = new javax.swing.JTextField();
+        bestTextField = new javax.swing.JTextField();
+        jLabel5 = new javax.swing.JLabel();
+        candTextField = new javax.swing.JTextField();
+        candReassignButton = new javax.swing.JButton();
+        jLabel6 = new javax.swing.JLabel();
+        mainZoomButton = new javax.swing.JButton();
+
+        getContentPane().setLayout(new java.awt.GridLayout(1, 0));
+
+        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
+        candZoomButton.setText("     ");
+        candZoomButton.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                candZoomButtonActionPerformed(evt);
+            }
+        });
+
+        bestZoomButton.setText("     ");
+        bestZoomButton.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                bestZoomButtonActionPerformed(evt);
+            }
+        });
+
+        elemConflictTree.addTreeSelectionListener(new javax.swing.event.TreeSelectionListener() {
+            public void valueChanged(javax.swing.event.TreeSelectionEvent evt) {
+                treeValueChanged(evt);
+            }
+        });
+
+        jScrollPane1.setViewportView(elemConflictTree);
+
+        jTabbedPane1.addTab("Dle datab\u00e1ze", jScrollPane1);
+
+        primConflictTree.addTreeSelectionListener(new javax.swing.event.TreeSelectionListener() {
+            public void valueChanged(javax.swing.event.TreeSelectionEvent evt) {
+                treeValueChanged(evt);
+            }
+        });
+
+        jScrollPane2.setViewportView(primConflictTree);
+
+        jTabbedPane1.addTab("Dle mapy", jScrollPane2);
+
+        jLabel4.setText("Objekt v konfliktu:");
+
+        mainTextField.setEditable(false);
+
+        bestTextField.setEditable(false);
+
+        jLabel5.setText("Nejlep\u0161\u00ed p\u0159i\u0159azen\u00ed:");
+
+        candTextField.setEditable(false);
+
+        candReassignButton.setText("P\u0159i\u0159adit");
+        candReassignButton.setEnabled(false);
+        candReassignButton.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                candReassignButtonActionPerformed(evt);
+            }
+        });
+
+        jLabel6.setText("Probl\u00e9mov\u00fd element:");
+
+        mainZoomButton.setText("     ");
+        mainZoomButton.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                mainZoomButtonActionPerformed(evt);
+            }
+        });
+
+        javax.swing.GroupLayout mainPanelLayout = new javax.swing.GroupLayout(mainPanel);
+        mainPanel.setLayout(mainPanelLayout);
+        mainPanelLayout.setHorizontalGroup(
+            mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, mainPanelLayout.createSequentialGroup()
+                .addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                    .addComponent(jLabel4)
+                    .addComponent(jLabel5)
+                    .addComponent(jLabel6))
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, mainPanelLayout.createSequentialGroup()
+                        .addComponent(candTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 232, Short.MAX_VALUE)
+                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                        .addComponent(candReassignButton))
+                    .addComponent(bestTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 319, Short.MAX_VALUE)
+                    .addComponent(mainTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 319, Short.MAX_VALUE))
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+                    .addComponent(mainZoomButton)
+                    .addComponent(bestZoomButton)
+                    .addComponent(candZoomButton)))
+            .addComponent(jTabbedPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 525, Short.MAX_VALUE)
+        );
+        mainPanelLayout.setVerticalGroup(
+            mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, mainPanelLayout.createSequentialGroup()
+                .addComponent(jTabbedPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 199, Short.MAX_VALUE)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(mainTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                    .addComponent(mainZoomButton)
+                    .addComponent(jLabel4))
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(bestTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                    .addComponent(bestZoomButton)
+                    .addComponent(jLabel5))
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(candReassignButton)
+                    .addComponent(candTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                    .addComponent(candZoomButton)
+                    .addComponent(jLabel6)))
+        );
+        getContentPane().add(mainPanel);
+
+        pack();
+    }// </editor-fold>//GEN-END:initComponents
+
+    private void treeValueChanged(javax.swing.event.TreeSelectionEvent evt) {//GEN-FIRST:event_treeValueChanged
+        selMainObj = null;
+        selBestFit = null;
+        selCandObj = null;
+
+        Reasoner r = CzechAddressPlugin.getReasoner();
+        TreePath tp = evt.getPath();
+
+        if (tp.getPathCount() >= 2)
+            selMainObj = tp.getPathComponent(1);
+
+        if (selMainObj instanceof AddressElement)
+            selBestFit = r.translate((AddressElement) selMainObj);
+        else if (selMainObj instanceof OsmPrimitive)
+            selBestFit = r.translate((OsmPrimitive) selMainObj);
+
+        mainTextField.setText(AddressElement.getName(selMainObj));
+        bestTextField.setText(AddressElement.getName(selBestFit));
+
+        if (tp.getPathCount() >= 3)
+            selCandObj = tp.getPathComponent(2);
+
+        candTextField.setText(AddressElement.getName(selCandObj));
+
+        if (selCandObj != null) {
+            if (selBestFit == null)
+                bestTextField.setText(
+                       "žádný neexistuje, objekty v konfliktu jsou rovnocenné");
+
+            candReassignButton.setEnabled(true);
+        } else
+            candReassignButton.setEnabled(false);
+
+        mainZoomButton.setEnabled(selMainObj instanceof OsmPrimitive);
+        bestZoomButton.setEnabled(selBestFit instanceof OsmPrimitive);
+        candZoomButton.setEnabled(selCandObj instanceof OsmPrimitive);
+
+    }//GEN-LAST:event_treeValueChanged
+
+    private void mainZoomButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_mainZoomButtonActionPerformed
+        assert selMainObj instanceof OsmPrimitive;
+        MapUtils.zoomTo((OsmPrimitive) selMainObj);
+    }//GEN-LAST:event_mainZoomButtonActionPerformed
+
+    private void bestZoomButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bestZoomButtonActionPerformed
+        assert selBestFit instanceof OsmPrimitive;
+        MapUtils.zoomTo((OsmPrimitive) selBestFit);
+    }//GEN-LAST:event_bestZoomButtonActionPerformed
+
+    private void candZoomButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_candZoomButtonActionPerformed
+        assert selCandObj instanceof OsmPrimitive;
+        MapUtils.zoomTo((OsmPrimitive) selCandObj);
+    }//GEN-LAST:event_candZoomButtonActionPerformed
+
+    private void candReassignButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_candReassignButtonActionPerformed
+        Reasoner r = CzechAddressPlugin.getReasoner();
+
+        if (    (selMainObj instanceof OsmPrimitive)
+             && (selCandObj instanceof AddressElement)) {
+             
+             Match best = r.findMatch((AddressElement) selBestFit);
+             if (best != null) best.qualityChanged();
+
+             List<Match> competitors = r.getConflicts((OsmPrimitive) selMainObj);
+             if (competitors != null) for (Match competitor : competitors)
+                 competitor.qualityChanged();
+
+             r.overwriteMatch((AddressElement) selCandObj, (OsmPrimitive) selMainObj);
+        }
+
+        else if ((selMainObj instanceof AddressElement)
+              && (selCandObj instanceof OsmPrimitive)) {
+
+            Match best = r.findMatch((OsmPrimitive) selBestFit);
+            if (best != null) best.qualityChanged();
+
+            List<Match> competitors = r.getConflicts((AddressElement) selMainObj);
+            if (competitors != null) for (Match competitor : competitors)
+                 competitor.qualityChanged();
+
+            r.overwriteMatch((AddressElement) selMainObj, (OsmPrimitive) selCandObj);
+        }
+
+        else
+            assert false;
+    }//GEN-LAST:event_candReassignButtonActionPerformed
+
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JTextField bestTextField;
+    private javax.swing.JButton bestZoomButton;
+    private javax.swing.JButton candReassignButton;
+    private javax.swing.JTextField candTextField;
+    private javax.swing.JButton candZoomButton;
+    private javax.swing.JTree elemConflictTree;
+    private javax.swing.JLabel jLabel4;
+    private javax.swing.JLabel jLabel5;
+    private javax.swing.JLabel jLabel6;
+    private javax.swing.JScrollPane jScrollPane1;
+    private javax.swing.JScrollPane jScrollPane2;
+    private javax.swing.JTabbedPane jTabbedPane1;
+    private javax.swing.JPanel mainPanel;
+    private javax.swing.JTextField mainTextField;
+    private javax.swing.JButton mainZoomButton;
+    private javax.swing.JTree primConflictTree;
+    // End of variables declaration//GEN-END:variables
+
+    public void pluginStatusChanged(int message) {
+        
+        if (message == MESSAGE_CONFLICT_CHANGED) {
+            setVisible(true);
+
+            ((HalfCookedTreeModel) elemConflictTree.getModel()).notifyAllListeners();
+            ((HalfCookedTreeModel) primConflictTree.getModel()).notifyAllListeners();
+
+            /* TODO: For some reason the previous call does not repaint.
+             *       Lines below fix it for now.*/
+            elemConflictTree.setModel(new ElemTreeModel());
+            primConflictTree.setModel(new PrimTreeModel());
+        }
+    }
+
+
+//==============================================================================
+
+    private class ElemTreeModel extends HalfCookedTreeModel {
+
+        public ElemTreeModel() {
+            root = "Konflikty podle objektů v databázi";
+        }
+
+        public Object getChild(Object parent, int index) {
+            try {
+                Reasoner r = CzechAddressPlugin.getReasoner();
+
+                if (parent == root)
+                    return r.getElementsInConflict().get(index);
+
+                if (parent instanceof AddressElement)
+                    return r.getConflicts((AddressElement) parent).get(index).prim;
+
+            } catch (Exception e) {  }
+            return null;
+        }
+
+        public int getChildCount(Object parent) {
+            try {
+                Reasoner r = CzechAddressPlugin.getReasoner();
+
+                if (parent == root)
+                    return r.getElementsInConflict().size();
+
+                if (parent instanceof AddressElement)
+                    return r.getConflicts((AddressElement) parent).size();
+                
+            } catch (Exception exp) { }
+            return 0;
+        }
+
+        public int getIndexOfChild(Object parent, Object child) {
+            try{
+                Reasoner r = CzechAddressPlugin.getReasoner();
+
+                if (parent == root)
+                    return r.getElementsInConflict().indexOf(child);
+
+                if (parent instanceof AddressElement)
+                    return r.getConflicts((AddressElement) parent).indexOf(child);
+                
+            } catch (Exception exp) { }
+            return -1;
+        }
+    }
+
+
+    private class PrimTreeModel extends HalfCookedTreeModel {
+
+        public PrimTreeModel() {
+            root = "Podle objeků databáze";
+        }
+
+        public Object getChild(Object parent, int index) {
+            try {
+                Reasoner r = CzechAddressPlugin.getReasoner();
+
+                if (parent == root)
+                    return r.getPrimitivesInConflict().get(index);
+
+                if (parent instanceof OsmPrimitive)
+                    return r.getConflicts((OsmPrimitive) parent).get(index).elem;
+                
+            } catch (Exception exp) { }
+            return null;
+        }
+
+        public int getChildCount(Object parent) {
+
+            try {
+                Reasoner r = CzechAddressPlugin.getReasoner();
+
+                if (parent == root)
+                    return r.getPrimitivesInConflict().size();
+                if (parent instanceof OsmPrimitive)
+                    return r.getConflicts((OsmPrimitive) parent).size();
+
+            } catch (Exception exp) { }
+            return 0;
+        }
+
+        public int getIndexOfChild(Object parent, Object child) {
+            
+            try {
+                Reasoner r = CzechAddressPlugin.getReasoner();
+
+                if (parent == root) {
+                    return r.getPrimitivesInConflict().indexOf(child);
+
+                } else if (parent instanceof OsmPrimitive)
+                    return r.getConflicts((OsmPrimitive) parent).indexOf(child);
+                
+            } catch (Exception exp) { }
+            return -1;
+        }
+    }
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/FactoryDialog.form
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/FactoryDialog.form	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/FactoryDialog.form	(revision 15166)
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<Form version="1.3" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
+  <AuxValues>
+    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+    <AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,0,-71,0,0,1,34"/>
+  </AuxValues>
+
+  <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridLayout">
+    <Property name="columns" type="int" value="0"/>
+    <Property name="rows" type="int" value="1"/>
+  </Layout>
+  <SubComponents>
+    <Container class="javax.swing.JPanel" name="mainPanel">
+
+      <Layout>
+        <DimensionLayout dim="0">
+          <Group type="103" groupAlignment="0" attributes="0">
+              <Group type="102" attributes="0">
+                  <Component id="streetComboBox" pref="50" max="32767" attributes="0"/>
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Component id="relocateButton" min="-2" max="-2" attributes="0"/>
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Component id="ensureConsistencyButton" min="-2" max="-2" attributes="0"/>
+              </Group>
+              <Group type="102" alignment="0" attributes="0">
+                  <Component id="keepOddityCheckBox" pref="282" max="32767" attributes="0"/>
+                  <EmptySpace max="-2" attributes="0"/>
+              </Group>
+              <Component id="jScrollPane1" alignment="0" pref="290" max="32767" attributes="3"/>
+          </Group>
+        </DimensionLayout>
+        <DimensionLayout dim="1">
+          <Group type="103" groupAlignment="0" attributes="0">
+              <Group type="102" max="-2" attributes="0">
+                  <Group type="103" groupAlignment="3" attributes="0">
+                      <Component id="streetComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
+                      <Component id="relocateButton" alignment="3" min="-2" max="-2" attributes="0"/>
+                      <Component id="ensureConsistencyButton" alignment="3" min="-2" max="-2" attributes="0"/>
+                  </Group>
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Component id="jScrollPane1" pref="125" max="32767" attributes="0"/>
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Component id="keepOddityCheckBox" min="-2" max="-2" attributes="0"/>
+              </Group>
+          </Group>
+        </DimensionLayout>
+      </Layout>
+      <SubComponents>
+        <Container class="javax.swing.JScrollPane" name="jScrollPane1">
+          <AuxValues>
+            <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
+          </AuxValues>
+
+          <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+          <SubComponents>
+            <Component class="javax.swing.JList" name="houseList">
+              <Properties>
+                <Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.editors2.ListModelEditor">
+                  <StringArray count="1">
+                    <StringItem index="0" value=" "/>
+                  </StringArray>
+                </Property>
+                <Property name="selectionMode" type="int" value="0"/>
+                <Property name="enabled" type="boolean" value="false"/>
+                <Property name="focusable" type="boolean" value="false"/>
+              </Properties>
+              <Events>
+                <EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="houseListClicked"/>
+              </Events>
+            </Component>
+          </SubComponents>
+        </Container>
+        <Component class="javax.swing.JCheckBox" name="keepOddityCheckBox">
+          <Properties>
+            <Property name="selected" type="boolean" value="true"/>
+            <Property name="text" type="java.lang.String" value="Zachov&#xe1;vat sudost / lichost"/>
+            <Property name="enabled" type="boolean" value="false"/>
+          </Properties>
+        </Component>
+        <Component class="javax.swing.JButton" name="relocateButton">
+          <Properties>
+            <Property name="text" type="java.lang.String" value="Inicializovat"/>
+            <Property name="enabled" type="boolean" value="false"/>
+          </Properties>
+          <Events>
+            <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="relocateButtonActionPerformed"/>
+          </Events>
+        </Component>
+        <Component class="javax.swing.JComboBox" name="streetComboBox">
+          <Properties>
+            <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
+              <Connection code="streetModel" type="code"/>
+            </Property>
+            <Property name="enabled" type="boolean" value="false"/>
+            <Property name="focusable" type="boolean" value="false"/>
+          </Properties>
+        </Component>
+        <Component class="javax.swing.JButton" name="ensureConsistencyButton">
+          <Properties>
+            <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
+              <Connection code="ImageProvider.get(&quot;actions&quot;, &quot;refresh-small.png&quot;)" type="code"/>
+            </Property>
+            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
+              <Connection code="&quot;&quot;" type="code"/>
+            </Property>
+            <Property name="toolTipText" type="java.lang.String" value="Provede nov&#xe9; p&#x159;i&#x159;azen&#xed; prvk&#x16f; mapy na elementy datab&#xe1;ze.&#xa;Touto volbou se zru&#x161;&#xed; v&#x161;echny manu&#xe1;ln&#x11b; vy&#x159;e&#x161;en&#xe9; konflikty."/>
+            <Property name="enabled" type="boolean" value="false"/>
+          </Properties>
+          <Events>
+            <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="ensureConsistencyButtonActionPerformed"/>
+          </Events>
+        </Component>
+      </SubComponents>
+    </Container>
+  </SubComponents>
+</Form>
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/FactoryDialog.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/FactoryDialog.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/FactoryDialog.java	(revision 15166)
@@ -0,0 +1,553 @@
+package org.openstreetmap.josm.plugins.czechaddress.gui;
+
+import org.openstreetmap.josm.plugins.czechaddress.StringUtils;
+import java.awt.Component;
+import java.awt.Font;
+import java.awt.LayoutManager;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import javax.swing.DefaultListCellRenderer;
+import javax.swing.ImageIcon;
+import javax.swing.JList;
+import org.openstreetmap.josm.data.SelectionChangedListener;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.gui.dialogs.ToggleDialog;
+import org.openstreetmap.josm.plugins.czechaddress.CzechAddressPlugin;
+import org.openstreetmap.josm.plugins.czechaddress.MapUtils;
+import org.openstreetmap.josm.plugins.czechaddress.StatusListener;
+import org.openstreetmap.josm.plugins.czechaddress.addressdatabase.AddressElement;
+import org.openstreetmap.josm.plugins.czechaddress.addressdatabase.ElementWithHouses;
+import org.openstreetmap.josm.plugins.czechaddress.addressdatabase.ElementWithStreets;
+import org.openstreetmap.josm.plugins.czechaddress.addressdatabase.House;
+import org.openstreetmap.josm.plugins.czechaddress.addressdatabase.Street;
+import org.openstreetmap.josm.plugins.czechaddress.intelligence.Reasoner;
+import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.Shortcut;
+
+/**
+ * A dockable dialog for controlling the "one click" address node creation.
+ *
+ * @author Radomír Černoch, radomir.cernoch@gmail.com
+ */
+public class FactoryDialog extends ToggleDialog
+        implements SelectionChangedListener, StatusListener {
+
+    private static FactoryDialog singleton = null;
+    public static FactoryDialog getInstance() {
+        if (singleton == null)
+            singleton = new FactoryDialog();
+        return singleton;
+    }
+
+
+    HouseListModel  houseModel  = new HouseListModel();
+    StreetListModel streetModel = new StreetListModel();
+    
+    public FactoryDialog() {
+
+        super( "Továrna na adresy",
+               "envelope-scrollbar.png",
+               "Umožňuje rychlé vytváření adresních bodů „jedním kliknutím.“",
+                Shortcut.registerShortcut(
+                    "subwindow:addressfactory",
+                    "Přepnout: Továrna na adresy",
+                    KeyEvent.VK_T, Shortcut.GROUP_LAYER),
+                200);
+
+        // Hack the ToggleDialog to allow using NetBeans form editor
+        LayoutManager originalManager = getLayout();
+        initComponents();
+        setLayout(originalManager);
+        add(mainPanel);
+
+        // Register to all listeners
+        CzechAddressPlugin.addStatusListener(this);
+
+        // And init all listeners to data model
+        streetModel.notifyAllListeners();
+        houseModel.notifyAllListeners();
+
+        streetComboBox.setModel(streetModel);
+        streetComboBox.setRenderer(new StreetListRenderer());
+        houseList.setModel(houseModel);
+        houseList.setCellRenderer(new HouseListRenderer());
+    }
+
+    public void pluginStatusChanged(int message) {
+
+        if (message == MESSAGE_DATABASE_LOADED) {
+            relocateButton.setEnabled(true);
+            return;
+        }
+        
+        if (message == MESSAGE_LOCATION_CHANGED) {
+            DataSet.selListeners.add(this);
+
+            streetModel.setParent(CzechAddressPlugin.getLocation());
+            relocateButton.setText("Změnit místo");
+            streetComboBox.setEnabled(true);
+            houseList.setEnabled(true);
+            keepOddityCheckBox.setEnabled(true);
+            return;
+
+        }
+        
+        if (message == MESSAGE_REASONER_REASONED) {
+            ensureConsistencyButton.setEnabled(true);
+        }
+        
+        if (message == MESSAGE_MATCHES_CHANGED || message == MESSAGE_CONFLICT_CHANGED) {
+            houseModel.notifyAllListeners();
+            return;
+        }
+    }
+
+    public House getSelectedHouse() {
+        if (houseList.getSelectedValue() instanceof House)
+            return (House) houseList.getSelectedValue();
+        else
+            return null;
+    }
+
+    public boolean existsAvailableHouse() {
+        Reasoner r = CzechAddressPlugin.getReasoner();
+
+        int i = houseList.getSelectedIndex();
+        while (i < houseModel.getSize()) {
+            if (r.translate((House) houseModel.getElementAt(i)) == null)
+                return true;
+            i++;
+        }
+
+        i = 0;
+        while (i < houseList.getSelectedIndex()) {
+            if (r.translate((House) houseModel.getElementAt(i)) == null)
+                return true;
+            i++;
+        }
+
+        return false;
+    }
+
+    public void selectNextUnmatchedHouse() {
+
+        Reasoner r = CzechAddressPlugin.getReasoner();
+        int index = houseList.getSelectedIndex();
+
+        index++;
+        Object current;
+        while ( (current = houseModel.getElementAt(index)) != null
+                && r.translate((House) current) != null)
+            index++;
+
+        if (index >= houseModel.getSize())
+            index = 0;
+
+        houseList.setSelectedIndex(index);
+        houseList.ensureIndexIsVisible(index);
+    }
+
+    public void selectNextUnmatchedHouseMaintainOddity() {
+
+        if (getSelectedHouse().getCO() == null) {
+            selectNextUnmatchedHouse();
+            return;
+        }
+
+        String oldStr = StringUtils.extractNumber(getSelectedHouse().getCO());
+
+        try {
+            int oldNum = -1, newNum = -1;
+            do {
+                selectNextUnmatchedHouse();
+                    
+                String newStr = StringUtils.extractNumber(getSelectedHouse().getCO());
+
+                if (oldNum == -1)
+                    oldNum = Integer.valueOf(oldStr);
+                newNum = Integer.valueOf(newStr);
+
+            } while ( (oldNum + newNum) % 2 == 1 &&
+                      houseList.getSelectedIndex() != 0 );
+            
+        } catch (Exception exp) {}
+    }
+
+    public void selectNextUnmatchedHouseByCheckBox() {
+        if (keepOddityCheckBox.isSelected())
+            selectNextUnmatchedHouseMaintainOddity();
+        else
+            selectNextUnmatchedHouse();
+    }
+
+    public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
+
+        if (newSelection.size() != 1) return;
+
+        OsmPrimitive selectedPrim = (OsmPrimitive) newSelection.toArray()[0];
+
+        String streetName;
+
+        if ((streetName = selectedPrim.get("addr:street")) == null) {
+            if (selectedPrim.get("highway") == null)
+                return;
+            else
+                if ((streetName = selectedPrim.get("name")) == null)
+                    return;
+        }
+
+        Street selectedStreet = null;
+        for (Street street : CzechAddressPlugin.getLocation().getStreets())
+            if (street.getName().toUpperCase().equals(streetName.toUpperCase())) {
+                selectedStreet = street;
+                break;
+            }
+
+        if (selectedStreet == null) return;
+        streetComboBox.setSelectedItem(selectedStreet);
+        streetModel.notifyAllListeners();
+
+        int bestQuality = -5;
+        House bestHouse = null;
+        for (House currHouse : selectedStreet.getHouses()) {
+
+            int currQuality = currHouse.getMatchQuality(selectedPrim);
+
+            if (currQuality > bestQuality) {
+                bestQuality = currQuality;
+                bestHouse = currHouse;
+            }
+        }
+
+        if (bestHouse == null) return;
+        houseList.setSelectedValue(bestHouse, true);
+        houseModel.notifyAllListeners();
+    }
+
+    /** This method is called from within the constructor to
+     * initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is
+     * always regenerated by the Form Editor.
+     */
+    @SuppressWarnings("unchecked")
+    // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
+    private void initComponents() {
+        mainPanel = new javax.swing.JPanel();
+        jScrollPane1 = new javax.swing.JScrollPane();
+        houseList = new javax.swing.JList();
+        keepOddityCheckBox = new javax.swing.JCheckBox();
+        relocateButton = new javax.swing.JButton();
+        streetComboBox = new javax.swing.JComboBox();
+        ensureConsistencyButton = new javax.swing.JButton();
+
+        setLayout(new java.awt.GridLayout(1, 0));
+
+        houseList.setModel(new javax.swing.AbstractListModel() {
+            String[] strings = { " " };
+            public int getSize() { return strings.length; }
+            public Object getElementAt(int i) { return strings[i]; }
+        });
+        houseList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
+        houseList.setEnabled(false);
+        houseList.setFocusable(false);
+        houseList.addMouseListener(new java.awt.event.MouseAdapter() {
+            public void mouseClicked(java.awt.event.MouseEvent evt) {
+                houseListClicked(evt);
+            }
+        });
+
+        jScrollPane1.setViewportView(houseList);
+
+        keepOddityCheckBox.setSelected(true);
+        keepOddityCheckBox.setText("Zachov\u00e1vat sudost / lichost");
+        keepOddityCheckBox.setEnabled(false);
+
+        relocateButton.setText("Inicializovat");
+        relocateButton.setEnabled(false);
+        relocateButton.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                relocateButtonActionPerformed(evt);
+            }
+        });
+
+        streetComboBox.setModel(streetModel);
+        streetComboBox.setEnabled(false);
+        streetComboBox.setFocusable(false);
+
+        ensureConsistencyButton.setIcon(ImageProvider.get("actions", "refresh-small.png"));
+        ensureConsistencyButton.setText("");
+        ensureConsistencyButton.setToolTipText("Provede nov\u00e9 p\u0159i\u0159azen\u00ed prvk\u016f mapy na elementy datab\u00e1ze.\nTouto volbou se zru\u0161\u00ed v\u0161echny manu\u00e1ln\u011b vy\u0159e\u0161en\u00e9 konflikty.");
+        ensureConsistencyButton.setEnabled(false);
+        ensureConsistencyButton.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                ensureConsistencyButtonActionPerformed(evt);
+            }
+        });
+
+        javax.swing.GroupLayout mainPanelLayout = new javax.swing.GroupLayout(mainPanel);
+        mainPanel.setLayout(mainPanelLayout);
+        mainPanelLayout.setHorizontalGroup(
+            mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(mainPanelLayout.createSequentialGroup()
+                .addComponent(streetComboBox, 0, 50, Short.MAX_VALUE)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(relocateButton)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(ensureConsistencyButton))
+            .addGroup(mainPanelLayout.createSequentialGroup()
+                .addComponent(keepOddityCheckBox, javax.swing.GroupLayout.DEFAULT_SIZE, 282, Short.MAX_VALUE)
+                .addContainerGap())
+            .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 290, Short.MAX_VALUE)
+        );
+        mainPanelLayout.setVerticalGroup(
+            mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(mainPanelLayout.createSequentialGroup()
+                .addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(streetComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                    .addComponent(relocateButton)
+                    .addComponent(ensureConsistencyButton))
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 125, Short.MAX_VALUE)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(keepOddityCheckBox))
+        );
+        add(mainPanel);
+
+    }// </editor-fold>//GEN-END:initComponents
+
+    private void relocateButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_relocateButtonActionPerformed
+        CzechAddressPlugin.changeLocation();
+    }//GEN-LAST:event_relocateButtonActionPerformed
+
+    private void houseListClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_houseListClicked
+        if (evt.getClickCount() == 2 && evt.getButton() == MouseEvent.BUTTON1) {
+            Reasoner r = CzechAddressPlugin.getReasoner();
+
+            if (r.translate(getSelectedHouse()) != null) {
+                MapUtils.zoomTo(r.translate(getSelectedHouse()));
+                return;
+            }
+
+            // TODO: The following code does not work... for some reason.
+            /*List<Match> conflicts = r.getConflictsForElement(getSelectegetConflicts if (conflicts != null) {
+                List<OsmPrimitive> toZoom
+                        = new ArrayList<OsmPrimitive>(conflicts.size());
+                for (Match conflict : conflicts)
+                    toZoom.add(conflict.prim);
+
+                MapUtils.zoomToMany(toZoom);
+                return;
+            }*/
+        }
+    }//GEN-LAST:event_houseListClicked
+
+    private void ensureConsistencyButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_ensureConsistencyButtonActionPerformed
+        CzechAddressPlugin.getReasoner().ensureConsistency();
+}//GEN-LAST:event_ensureConsistencyButtonActionPerformed
+
+
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JButton ensureConsistencyButton;
+    private javax.swing.JList houseList;
+    private javax.swing.JScrollPane jScrollPane1;
+    private javax.swing.JCheckBox keepOddityCheckBox;
+    private javax.swing.JPanel mainPanel;
+    private javax.swing.JButton relocateButton;
+    private javax.swing.JComboBox streetComboBox;
+    // End of variables declaration//GEN-END:variables
+
+    private class StreetListRenderer extends DefaultListCellRenderer {
+
+        Font plainFont = null;
+        Font boldFont = null;
+
+        @Override
+        public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
+            Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
+
+            if (plainFont == null) plainFont = getFont().deriveFont(Font.PLAIN);
+            if (boldFont  == null)  boldFont = getFont().deriveFont(Font.BOLD);
+
+            if (value instanceof Street) {
+                setFont(plainFont);
+                setText(((Street) value).getName());
+                
+            } else {
+                setFont(boldFont);
+                if (value instanceof ElementWithStreets) setText("[domy bez ulice]");
+                if (value instanceof AllStreetProvider)  setText("[všechny domy]");
+                if (value instanceof FreeStreetProvider) setText("[nepřiřazené domy]");
+
+            }
+
+            return c;
+        }
+    }
+
+    private class HouseListRenderer extends DefaultListCellRenderer {
+
+        Font plainFont = null;
+        Font boldFont = null;
+
+        ImageIcon envelopeNormIcon = ImageProvider.get("envelope-closed-small.png");
+        ImageIcon envelopeStarIcon = ImageProvider.get("envelope-closed-star-small.png");
+        ImageIcon envelopeExclIcon = ImageProvider.get("envelope-closed-exclamation-small.png");
+
+        @Override
+        public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
+            Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
+
+            if (plainFont == null) plainFont = getFont().deriveFont(Font.PLAIN);
+            if (boldFont == null) boldFont = getFont().deriveFont(Font.BOLD);
+
+            Reasoner r = CzechAddressPlugin.getReasoner();
+
+            if (value instanceof House) {
+                House house = (House) value;
+
+                setIcon(envelopeNormIcon);
+                setFont(plainFont);
+
+                if ( r.getConflicts(house) != null)
+                    setIcon(envelopeExclIcon);
+                else if ( r.translate(house) == null) {
+                    setIcon(envelopeStarIcon);
+                    setFont(boldFont);
+                }
+
+                setText(house.getName());
+            }
+            return c;
+        }
+    }
+
+    private class AllStreetProvider extends ElementWithHouses {
+        public AllStreetProvider() {
+            super("Všechny domy");
+        }
+
+        @Override
+        public void setHouses(List<House> houses) {
+            this.houses = houses;
+        }
+    }
+
+    private class FreeStreetProvider extends ElementWithHouses
+                                     implements StatusListener {
+
+        public FreeStreetProvider() {
+            super("Nepřiřazené domy");
+            CzechAddressPlugin.addStatusListener(this);
+            rebuild();
+        }
+
+        @Override
+        public void setHouses(List<House> houses) {
+            this.houses = houses;
+        }
+
+        public void pluginStatusChanged(int message) {
+            if (message == StatusListener.MESSAGE_MATCHES_CHANGED)
+                rebuild();
+        }
+
+        public void rebuild() {
+            Reasoner r = CzechAddressPlugin.getReasoner();
+            if (r == null) return;
+
+            houses.clear();
+            for (AddressElement house : r.getElementPool())
+                if (r.translate(house) == null)
+                    if (house instanceof House)
+                        houses.add((House) house);
+        }
+    }
+
+    private class StreetListModel extends HalfCookedComboBoxModel {
+
+        private ElementWithHouses selected = null;
+        private ElementWithStreets parent = null;
+
+        private List<ElementWithHouses> metaElem
+                = new ArrayList<ElementWithHouses>();
+
+        public StreetListModel() {
+            metaElem.add(null);
+            metaElem.add(new AllStreetProvider());
+            metaElem.add(new FreeStreetProvider());
+        }
+
+
+
+        public int getSize() {
+            if (parent == null) return 0;
+            return parent.getStreets().size() + metaElem.size();
+        }
+
+        public void setParent(ElementWithStreets parent) {
+            if (parent == null) return;
+
+            selected = parent;
+            this.parent = parent;
+            metaElem.set(0, parent);
+
+            metaElem.get(1).setHouses(parent.getAllHouses());
+            notifyAllListeners();
+        }
+
+        public Object getElementAt(int index) {
+
+            if (parent == null) return null;
+            if (index <  0) return null;
+
+            if (index < metaElem.size())
+                return metaElem.get(index);
+
+            index -= metaElem.size();
+            // Now the index points to the list of streets
+            if (index < parent.getStreets().size())
+                return parent.getStreets().get(index);
+
+            return null;
+        }
+
+        public void setSelectedItem(Object anItem) {
+            assert anItem instanceof ElementWithHouses;
+            selected = (ElementWithHouses) anItem;
+            houseModel.notifyAllListeners();
+        }
+
+        public Object getSelectedItem() {
+            return selected;
+        }
+    }
+
+    private class HouseListModel extends HalfCookedListModel {
+
+        public int getSize() {
+            if (streetComboBox.getSelectedItem() == null) return 0;
+            ElementWithHouses selected
+                    = (ElementWithHouses) streetComboBox.getSelectedItem();
+
+            return selected.getHouses().size();
+        }
+
+        public House getHouseAt(int index) {
+            if (streetComboBox.getSelectedItem() == null) return null;
+            ElementWithHouses selected
+                    = (ElementWithHouses) streetComboBox.getSelectedItem();
+
+            if ((index < 0) || (index >= selected.getHouses().size())) return null;
+            return selected.getHouses().get(index);
+        }
+
+        public Object getElementAt(int index) {
+            return getHouseAt(index);
+        }
+    }
+
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/GroupManipulatorDialog.form
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/GroupManipulatorDialog.form	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/GroupManipulatorDialog.form	(revision 15166)
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<Form version="1.3" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
+  <SyntheticProperties>
+    <SyntheticProperty name="formSizePolicy" type="int" value="2"/>
+  </SyntheticProperties>
+  <AuxValues>
+    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+    <AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,19,0,0,1,-30"/>
+  </AuxValues>
+
+  <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridLayout">
+    <Property name="columns" type="int" value="0"/>
+    <Property name="rows" type="int" value="1"/>
+  </Layout>
+  <SubComponents>
+    <Container class="javax.swing.JPanel" name="mainPanel">
+
+      <Layout>
+        <DimensionLayout dim="0">
+          <Group type="103" groupAlignment="0" attributes="0">
+              <Group type="102" alignment="0" attributes="0">
+                  <Component id="locationLabel" min="-2" max="-2" attributes="0"/>
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Component id="locationTextField" pref="367" max="32767" attributes="0"/>
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Component id="locationChangeButton" min="-2" max="-2" attributes="0"/>
+              </Group>
+              <Component id="jScrollPane1" alignment="0" pref="482" max="32767" attributes="0"/>
+              <Group type="102" alignment="0" attributes="0">
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Component id="jLabel3" pref="458" max="32767" attributes="0"/>
+                  <EmptySpace max="-2" attributes="0"/>
+              </Group>
+          </Group>
+        </DimensionLayout>
+        <DimensionLayout dim="1">
+          <Group type="103" groupAlignment="0" attributes="0">
+              <Group type="102" alignment="0" attributes="0">
+                  <Group type="103" groupAlignment="3" attributes="0">
+                      <Component id="locationTextField" alignment="3" min="-2" max="-2" attributes="0"/>
+                      <Component id="locationChangeButton" alignment="3" min="-2" max="-2" attributes="0"/>
+                      <Component id="locationLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+                  </Group>
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Component id="jScrollPane1" pref="221" max="32767" attributes="0"/>
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Component id="jLabel3" min="-2" max="-2" attributes="0"/>
+              </Group>
+          </Group>
+        </DimensionLayout>
+      </Layout>
+      <SubComponents>
+        <Component class="javax.swing.JLabel" name="locationLabel">
+          <Properties>
+            <Property name="text" type="java.lang.String" value="M&#xed;sto:"/>
+          </Properties>
+        </Component>
+        <Component class="javax.swing.JButton" name="locationChangeButton">
+          <Properties>
+            <Property name="text" type="java.lang.String" value="Zm&#x11b;nit"/>
+          </Properties>
+          <Events>
+            <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="locationChangeButtonActionPerformed"/>
+          </Events>
+        </Component>
+        <Component class="javax.swing.JTextField" name="locationTextField">
+          <Properties>
+            <Property name="editable" type="boolean" value="false"/>
+          </Properties>
+        </Component>
+        <Component class="javax.swing.JLabel" name="jLabel3">
+          <Properties>
+            <Property name="text" type="java.lang.String" value="&#x2022; Pro smaz&#xe1;n&#xed; n&#xe1;vrhu pou&#x17e;ijte kl&#xe1;vesu Del."/>
+            <Property name="verticalAlignment" type="int" value="1"/>
+          </Properties>
+        </Component>
+        <Container class="javax.swing.JScrollPane" name="jScrollPane1">
+          <AuxValues>
+            <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
+          </AuxValues>
+
+          <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+          <SubComponents>
+            <Component class="javax.swing.JTree" name="proposalTree">
+              <Events>
+                <EventHandler event="keyReleased" listener="java.awt.event.KeyListener" parameters="java.awt.event.KeyEvent" handler="proposalTreeKeyReleased"/>
+              </Events>
+            </Component>
+          </SubComponents>
+        </Container>
+      </SubComponents>
+    </Container>
+  </SubComponents>
+</Form>
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/GroupManipulatorDialog.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/GroupManipulatorDialog.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/GroupManipulatorDialog.java	(revision 15166)
@@ -0,0 +1,212 @@
+package org.openstreetmap.josm.plugins.czechaddress.gui;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import javax.swing.tree.TreePath;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.ExtendedDialog;
+import org.openstreetmap.josm.plugins.czechaddress.CzechAddressPlugin;
+import org.openstreetmap.josm.plugins.czechaddress.StatusListener;
+import org.openstreetmap.josm.plugins.czechaddress.intelligence.Reasoner;
+import org.openstreetmap.josm.plugins.czechaddress.proposal.Proposal;
+import org.openstreetmap.josm.plugins.czechaddress.proposal.ProposalContainer;
+import org.openstreetmap.josm.plugins.czechaddress.proposal.ProposalDatabase;
+
+/**
+ * A dialog window proposing changes to addresses. It allows to browse the
+ * proposals, delete or confirm them.
+ *
+ * Apart from proposals, it also shows a list of conflicts, which arouse
+ * during the address completion.
+ *
+ * @author Radomír Černoch radomir.cernoch@gmail.com
+ *
+ * @see ProposalDatabase
+ * @see ConflictDatabase
+ */
+public class GroupManipulatorDialog extends ExtendedDialog implements StatusListener {
+
+    private static GroupManipulatorDialog singleton = null;
+    public static GroupManipulatorDialog getInstence() {
+        if (singleton == null)
+            singleton = new GroupManipulatorDialog();
+        return singleton;
+    }
+
+    /**
+     * Creates a new dialog window and sets the list of primitives
+     * to be changed. The proposals are created automatically.
+     * @param data set of primitives, which can be changed
+     */
+    public GroupManipulatorDialog() {
+        super(Main.parent, "Přiřadit adresy",
+                            new String[] { "OK", "Zrušit" }, true);
+        initComponents();
+
+        // Do some nice-look stuff.
+        proposalTree.setCellRenderer(new UniversalTreeRenderer());
+
+        // And finalize initializing the form.
+        setupDialog(mainPanel, new String[] { "ok.png", "cancel.png" });
+        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
+        setAlwaysOnTop(false);
+
+        // Start receiving plugin-wide messages.
+        CzechAddressPlugin.addStatusListener(this);
+
+        // TODO: Why does it always crash if the modality is set in constructor?
+        setModal(false);
+    }
+
+    @Override
+    protected void buttonAction(ActionEvent evt) {
+        super.buttonAction(evt);
+        if (getValue() == 1)
+            ((ProposalDatabase) proposalTree.getModel()).applyAll();
+    }
+
+    public void pluginStatusChanged(int message) {
+        if (message == StatusListener.MESSAGE_MATCHES_CHANGED) {
+            int retval = (new ExtendedDialog(Main.parent, "Změna umístění",
+                    "Došlo ke změně v přiřazení databáze.\n" +
+                    "Přejete si znovu načíst seznam navrhovaných změn?",
+                    new String[] {"Ano", "Ne"},
+                    new String[] {"ok.png", "cancel.png"})).getValue();
+
+            if (retval == 1)
+                recreateProposals();
+        }
+    }
+
+    @Override
+    public void setVisible(boolean b) {
+        if (!isVisible() && b)
+            recreateProposals();
+
+        if (b)
+            CzechAddressPlugin.addStatusListener(this);
+        else
+            CzechAddressPlugin.removeStatusListener(this);
+
+        super.setVisible(b);
+    }
+
+    public void recreateProposals() {
+        locationTextField.setText(CzechAddressPlugin.getLocation().toString());
+        
+        Reasoner r = CzechAddressPlugin.getReasoner();
+        proposalTree.setModel(r.getProposals());
+    }
+
+    /**
+     * This method is called from within the constructor to initialize the form.
+     * 
+     * <p><b>WARNING:</b> Do NOT modify this code. The content of this method is
+     * always regenerated by the Netbeans Form Editor.</p>
+     */
+    @SuppressWarnings("unchecked")
+    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+    private void initComponents() {
+
+        mainPanel = new javax.swing.JPanel();
+        locationLabel = new javax.swing.JLabel();
+        locationChangeButton = new javax.swing.JButton();
+        locationTextField = new javax.swing.JTextField();
+        jLabel3 = new javax.swing.JLabel();
+        jScrollPane1 = new javax.swing.JScrollPane();
+        proposalTree = new javax.swing.JTree();
+
+        setLayout(new java.awt.GridLayout());
+
+        locationLabel.setText("Místo:");
+
+        locationChangeButton.setText("Změnit");
+        locationChangeButton.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                locationChangeButtonActionPerformed(evt);
+            }
+        });
+
+        locationTextField.setEditable(false);
+
+        jLabel3.setText("• Pro smazání návrhu použijte klávesu Del.");
+        jLabel3.setVerticalAlignment(javax.swing.SwingConstants.TOP);
+
+        proposalTree.addKeyListener(new java.awt.event.KeyAdapter() {
+            public void keyReleased(java.awt.event.KeyEvent evt) {
+                proposalTreeKeyReleased(evt);
+            }
+        });
+        jScrollPane1.setViewportView(proposalTree);
+
+        javax.swing.GroupLayout mainPanelLayout = new javax.swing.GroupLayout(mainPanel);
+        mainPanel.setLayout(mainPanelLayout);
+        mainPanelLayout.setHorizontalGroup(
+            mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(mainPanelLayout.createSequentialGroup()
+                .addComponent(locationLabel)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(locationTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 379, Short.MAX_VALUE)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(locationChangeButton))
+            .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 482, Short.MAX_VALUE)
+            .addGroup(mainPanelLayout.createSequentialGroup()
+                .addContainerGap()
+                .addComponent(jLabel3, javax.swing.GroupLayout.DEFAULT_SIZE, 458, Short.MAX_VALUE)
+                .addContainerGap())
+        );
+        mainPanelLayout.setVerticalGroup(
+            mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(mainPanelLayout.createSequentialGroup()
+                .addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(locationTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                    .addComponent(locationChangeButton)
+                    .addComponent(locationLabel))
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 223, Short.MAX_VALUE)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(jLabel3))
+        );
+
+        add(mainPanel);
+    }// </editor-fold>//GEN-END:initComponents
+
+    /**
+     * Event handler for clicking on the button, which changes
+     * the current location.
+     */
+    private void locationChangeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_locationChangeButtonActionPerformed
+        CzechAddressPlugin.changeLocation();
+    }//GEN-LAST:event_locationChangeButtonActionPerformed
+
+    private void proposalTreeKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_proposalTreeKeyReleased
+        if (evt.getKeyCode() == KeyEvent.VK_DELETE) {
+
+            for (TreePath path : proposalTree.getSelectionPaths()) {
+
+                if (path.getLastPathComponent() instanceof Proposal) {
+                    ProposalContainer pc = (ProposalContainer)
+                            path.getParentPath().getLastPathComponent();
+                    pc.removeProposal((Proposal) path.getLastPathComponent());
+
+
+                } else if (path.getLastPathComponent() instanceof ProposalContainer) {
+                    ((ProposalDatabase) proposalTree.getModel())
+                            .removeContainer( (ProposalContainer)
+                                                    path.getLastPathComponent() );
+                }
+            }
+        }
+    }//GEN-LAST:event_proposalTreeKeyReleased
+
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JLabel jLabel3;
+    private javax.swing.JScrollPane jScrollPane1;
+    private javax.swing.JButton locationChangeButton;
+    private javax.swing.JLabel locationLabel;
+    private javax.swing.JTextField locationTextField;
+    private javax.swing.JPanel mainPanel;
+    private javax.swing.JTree proposalTree;
+    // End of variables declaration//GEN-END:variables
+
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/HalfCookedComboBoxModel.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/HalfCookedComboBoxModel.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/HalfCookedComboBoxModel.java	(revision 15166)
@@ -0,0 +1,37 @@
+package org.openstreetmap.josm.plugins.czechaddress.gui;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.swing.ComboBoxModel;
+import javax.swing.event.ListDataEvent;
+import javax.swing.event.ListDataListener;
+
+/**
+ * Class for shorter and faster implementations of {@link ComboBoxModel}s.
+ *
+ * <p>This creates a list of {@link ListDataListener}s and implements
+ * method for adding and removing them. Moreover it allows to notify all
+ * listeners with the generic message {@code CONTENT_CHANGED}.</p>
+ *
+ * @author Radomír Černoch, radomir.cernoch@gmail.com
+ */
+public abstract class HalfCookedComboBoxModel implements ComboBoxModel {
+
+    List<ListDataListener> listeners = new ArrayList<ListDataListener>();
+
+    public void addListDataListener(ListDataListener l) {
+        listeners.add(l);
+    }
+
+    public void removeListDataListener(ListDataListener l) {
+        listeners.remove(l);
+    }
+
+    public void notifyAllListeners() {
+        ListDataEvent evt = new ListDataEvent(this,
+                ListDataEvent.CONTENTS_CHANGED, 0, getSize()-1);
+
+        for (ListDataListener l : listeners)
+            l.contentsChanged(evt);
+    }
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/HalfCookedListModel.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/HalfCookedListModel.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/HalfCookedListModel.java	(revision 15166)
@@ -0,0 +1,37 @@
+package org.openstreetmap.josm.plugins.czechaddress.gui;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.swing.ListModel;
+import javax.swing.event.ListDataEvent;
+import javax.swing.event.ListDataListener;
+
+/**
+ * Class for shorter and faster implementations of {@link ListModel}s.
+ *
+ * <p>This creates a list of {@link ListDataListener}s and implements
+ * method for adding and removing them. Moreover it allows to notify all
+ * listeners with the generic message {@code CONTENT_CHANGED}.</p>
+ *
+ * @author Radomír Černoch, radomir.cernoch@gmail.com
+ */
+public abstract class HalfCookedListModel implements ListModel {
+
+    List<ListDataListener> listeners = new ArrayList<ListDataListener>();
+
+    public void addListDataListener(ListDataListener l) {
+        listeners.add(l);
+    }
+
+    public void removeListDataListener(ListDataListener l) {
+        listeners.remove(l);
+    }
+
+    public void notifyAllListeners() {
+        ListDataEvent evt = new ListDataEvent(this,
+                ListDataEvent.CONTENTS_CHANGED, 0, getSize()-1);
+
+        for (ListDataListener l : listeners)
+            l.contentsChanged(evt);
+    }
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/HalfCookedTreeModel.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/HalfCookedTreeModel.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/HalfCookedTreeModel.java	(revision 15166)
@@ -0,0 +1,51 @@
+package org.openstreetmap.josm.plugins.czechaddress.gui;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.swing.event.TreeModelEvent;
+import javax.swing.event.TreeModelListener;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreePath;
+
+/**
+ * Class for shorter and faster implementations of {@link TreeModel}s.
+ *
+ * <p>This creates a list of {@link TreeModelListener}s and implements
+ * method for adding and removing them. Moreover it allows to notify all
+ * listeners with the generic message telling that the whole tree
+ * has changed.</p>
+ *
+ * @author Radomír Černoch, radomir.cernoch@gmail.com
+ */
+public abstract class HalfCookedTreeModel implements TreeModel {
+
+    List<TreeModelListener> listeneres = new ArrayList<TreeModelListener>();
+
+    protected String root;
+    public Object getRoot() {
+        return root;
+    }
+
+    public void addTreeModelListener(TreeModelListener l) {
+        listeneres.add(l);
+    }
+
+    public void removeTreeModelListener(TreeModelListener l) {
+        listeneres.remove(l);
+    }
+
+    public boolean isLeaf(Object node) {
+        return getChildCount(node) == 0;
+    }
+
+    public void valueForPathChanged(TreePath path, Object newValue) {
+
+    }
+
+    public void notifyAllListeners() {
+        TreeModelEvent evt = new TreeModelEvent(this, new Object[] {root});
+
+        for (TreeModelListener l : listeneres)
+            l.treeNodesChanged(evt);
+    }
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/LocationSelector.form
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/LocationSelector.form	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/LocationSelector.form	(revision 15166)
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<Form version="1.3" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
+  <Properties>
+    <Property name="title" type="java.lang.String" value="V&#xfd;b&#x11b;r um&#xed;st&#x11b;n&#xed;"/>
+    <Property name="modal" type="boolean" value="true"/>
+    <Property name="name" type="java.lang.String" value="locationSelector"/>
+  </Properties>
+  <SyntheticProperties>
+    <SyntheticProperty name="formSizePolicy" type="int" value="2"/>
+  </SyntheticProperties>
+  <AuxValues>
+    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+    <AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,0,99,0,0,1,-91"/>
+  </AuxValues>
+
+  <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridLayout">
+    <Property name="columns" type="int" value="0"/>
+    <Property name="rows" type="int" value="1"/>
+  </Layout>
+  <SubComponents>
+    <Container class="javax.swing.JPanel" name="mainPanel">
+
+      <Layout>
+        <DimensionLayout dim="0">
+          <Group type="103" groupAlignment="0" attributes="0">
+              <Group type="102" alignment="0" attributes="0">
+                  <Group type="103" groupAlignment="1" attributes="0">
+                      <Component id="oblastLabel" min="-2" max="-2" attributes="0"/>
+                      <Component id="obecLabel" min="-2" max="-2" attributes="0"/>
+                      <Component id="castObceLabel" min="-2" max="-2" attributes="0"/>
+                  </Group>
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Group type="103" groupAlignment="1" attributes="0">
+                      <Component id="castObceComboBox" pref="340" max="32767" attributes="0"/>
+                      <Component id="obecComboBox" pref="340" max="32767" attributes="0"/>
+                      <Component id="oblastComboBox" pref="340" max="32767" attributes="0"/>
+                  </Group>
+              </Group>
+          </Group>
+        </DimensionLayout>
+        <DimensionLayout dim="1">
+          <Group type="103" groupAlignment="0" attributes="0">
+              <Group type="102" alignment="0" attributes="0">
+                  <Group type="103" groupAlignment="3" attributes="0">
+                      <Component id="oblastLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+                      <Component id="oblastComboBox" alignment="3" min="-2" pref="25" max="-2" attributes="0"/>
+                  </Group>
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Group type="103" groupAlignment="3" attributes="0">
+                      <Component id="obecLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+                      <Component id="obecComboBox" alignment="3" min="-2" pref="25" max="-2" attributes="0"/>
+                  </Group>
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Group type="103" groupAlignment="3" attributes="0">
+                      <Component id="castObceLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+                      <Component id="castObceComboBox" alignment="3" min="-2" pref="25" max="-2" attributes="0"/>
+                  </Group>
+                  <EmptySpace max="32767" attributes="0"/>
+              </Group>
+          </Group>
+        </DimensionLayout>
+      </Layout>
+      <SubComponents>
+        <Component class="javax.swing.JComboBox" name="oblastComboBox">
+          <Properties>
+            <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
+              <StringArray count="0"/>
+            </Property>
+          </Properties>
+          <Events>
+            <EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="oblastComboBoxItemStateChanged"/>
+          </Events>
+        </Component>
+        <Component class="javax.swing.JComboBox" name="castObceComboBox">
+          <Properties>
+            <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
+              <StringArray count="0"/>
+            </Property>
+          </Properties>
+          <Events>
+            <EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="castObceComboBoxItemStateChanged"/>
+          </Events>
+        </Component>
+        <Component class="javax.swing.JComboBox" name="obecComboBox">
+          <Properties>
+            <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
+              <StringArray count="0"/>
+            </Property>
+          </Properties>
+          <Events>
+            <EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="obecComboBoxItemStateChanged"/>
+          </Events>
+        </Component>
+        <Component class="javax.swing.JLabel" name="obecLabel">
+          <Properties>
+            <Property name="text" type="java.lang.String" value="Obec:"/>
+          </Properties>
+        </Component>
+        <Component class="javax.swing.JLabel" name="castObceLabel">
+          <Properties>
+            <Property name="text" type="java.lang.String" value="&#x10c;&#xe1;st obce:"/>
+          </Properties>
+        </Component>
+        <Component class="javax.swing.JLabel" name="oblastLabel">
+          <Properties>
+            <Property name="text" type="java.lang.String" value="Oblast:"/>
+          </Properties>
+        </Component>
+      </SubComponents>
+    </Container>
+  </SubComponents>
+</Form>
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/LocationSelector.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/LocationSelector.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/LocationSelector.java	(revision 15166)
@@ -0,0 +1,343 @@
+package org.openstreetmap.josm.plugins.czechaddress.gui;
+
+import org.openstreetmap.josm.plugins.czechaddress.*;
+import java.awt.Component;
+import java.util.ArrayList;
+
+import java.awt.event.ItemListener;
+
+
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.DefaultListCellRenderer;
+import javax.swing.JList;
+import org.openstreetmap.josm.plugins.czechaddress.addressdatabase.AddressElement;
+import org.openstreetmap.josm.plugins.czechaddress.addressdatabase.Suburb;
+import org.openstreetmap.josm.plugins.czechaddress.addressdatabase.ElementWithStreets;
+import org.openstreetmap.josm.plugins.czechaddress.addressdatabase.ViToCi;
+import org.openstreetmap.josm.plugins.czechaddress.addressdatabase.Region;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
+import org.openstreetmap.josm.gui.ExtendedDialog;
+
+/**
+ * Dialog for selecting the current loaction.
+ *
+ * @author Radomír Černoch, radomir.cernoch@gmail.com
+ */
+public class LocationSelector extends ExtendedDialog {
+
+    protected ElementWithStreets selectedElement;
+    protected ArrayList<ItemListener> listeners = new ArrayList<ItemListener>();
+
+
+    public static ElementWithStreets selectLocation() {
+        LocationSelector ls = new LocationSelector();
+        ls.setVisible(true);
+        
+        if (ls.getValue() == 1)
+            return ls.selectedElement;
+        else
+            return null;
+    }
+
+    private LocationSelector() {
+        super(Main.parent, "Výběr umístění",
+                            new String[] { "OK", "Zrušit"}, true);
+        
+        initComponents();
+        setupDialog(mainPanel, new String[] { "ok.png", "cancel.png"});
+
+        oblastComboBox.setRenderer(new AddressElementRenderer());
+        obecComboBox.setRenderer(new AddressElementRenderer());
+        castObceComboBox.setRenderer(new AddressElementRenderer());
+
+        oblastComboBox.setModel(new DefaultComboBoxModel(
+                CzechAddressPlugin.getDatabase().regions.toArray()));
+
+        autodetectLocation();
+        oblastComboBoxItemStateChanged(null);
+    }
+
+    /**
+     * @deprecated 
+     */
+    private void autodetectLocation() {
+        boolean assertions = false;
+        assert  assertions = true;
+
+        OsmPrimitive bestFit = null;
+        double bestLen = 0;
+
+        // TODO: center se počítá jako střed stažené oblasti. Měl by to však
+        // být střed obrazovky... Jen vědět, jak získat souřadnici středu
+        // obrazovky.
+
+        BoundingXYVisitor visitor = new BoundingXYVisitor();
+        for (OsmPrimitive op : Main.ds.allPrimitives()) {
+            if (op instanceof Node) {
+                ((Node) op).visit(visitor);
+            } else if (op instanceof Way) {
+                ((Way) op).visit(visitor);
+            }
+        }
+
+        LatLon center;
+
+        try {
+            Bounds bounds = visitor.getBounds();
+            LatLon max = bounds.max;
+            LatLon min = bounds.min;
+            center = new LatLon(
+                    (max.getX() + min.getX()) / 2,
+                    (max.getY() + min.getY()) / 2);
+
+        } catch (Exception e) {
+            System.err.println("AUTO: No bounds to determine autolocation.");
+            return;
+        }
+
+        if (assertions)
+            System.out.println("AUTO: Center is " + center);
+
+        for (OsmPrimitive op : Main.ds.allPrimitives()) {
+
+            if (!(op instanceof Node)) {
+                continue;
+            }
+            Node node = (Node) op;
+
+            double multiplicator = 5;
+            if (new String("city").equals(op.get("place"))) {
+                multiplicator = 2.8;
+            } else if (new String("town").equals(op.get("place"))) {
+                multiplicator = 2.3;
+            } else if (new String("village").equals(op.get("place"))) {
+                multiplicator = 2;
+            } else if (new String("suburb").equals(op.get("place"))) {
+                multiplicator = 1;
+            } else {
+                continue;
+            }
+
+            double currLen = multiplicator * (node.coor.distance(center));
+
+
+            if ((bestFit == null) || (currLen < bestLen)) {
+                bestFit = op;
+                bestLen = currLen;
+            }
+        }
+
+        if (bestFit != null) {
+            
+            if (assertions)
+                System.out.println("AUTO: Best fit " + bestFit.getName()
+                                 + "\t " + bestFit.get("name"));
+            
+            for (Region oblast : CzechAddressPlugin.getDatabase().regions) {
+                for (ViToCi obec : oblast.getViToCis()) {
+                    if (!bestFit.get("place").equals("suburb")) {
+                        if (obec.getName().toUpperCase().equals(bestFit.get("name").toUpperCase())) {
+                            oblastComboBox.setSelectedItem(oblast);
+                            obecComboBox.setSelectedItem(obec);
+                            for (Suburb castObce : obec.getSuburbs()) {
+                                if (castObce.getName().toUpperCase().equals(bestFit.get("name").toUpperCase())) {
+                                    castObceComboBox.setSelectedItem(castObce);
+                                    break;
+                                }
+                            }
+                            break;
+                        }
+                    } else {
+                        for (Suburb castObce : obec.getSuburbs()) {
+                            if (castObce.getName().toUpperCase().equals(bestFit.get("name").toUpperCase())) {
+                                oblastComboBox.setSelectedItem(oblast);
+                                obecComboBox.setSelectedItem(obec);
+                                castObceComboBox.setSelectedItem(castObce);
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /** This method is called from within the constructor to
+     * initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is
+     * always regenerated by the Form Editor.
+     */
+    @SuppressWarnings("unchecked")
+    // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
+    private void initComponents() {
+        mainPanel = new javax.swing.JPanel();
+        oblastComboBox = new javax.swing.JComboBox();
+        castObceComboBox = new javax.swing.JComboBox();
+        obecComboBox = new javax.swing.JComboBox();
+        obecLabel = new javax.swing.JLabel();
+        castObceLabel = new javax.swing.JLabel();
+        oblastLabel = new javax.swing.JLabel();
+
+        getContentPane().setLayout(new java.awt.GridLayout(1, 0));
+
+        setTitle("V\u00fdb\u011br um\u00edst\u011bn\u00ed");
+        setModal(true);
+        setName("locationSelector");
+        oblastComboBox.addItemListener(new java.awt.event.ItemListener() {
+            public void itemStateChanged(java.awt.event.ItemEvent evt) {
+                oblastComboBoxItemStateChanged(evt);
+            }
+        });
+
+        castObceComboBox.addItemListener(new java.awt.event.ItemListener() {
+            public void itemStateChanged(java.awt.event.ItemEvent evt) {
+                castObceComboBoxItemStateChanged(evt);
+            }
+        });
+
+        obecComboBox.addItemListener(new java.awt.event.ItemListener() {
+            public void itemStateChanged(java.awt.event.ItemEvent evt) {
+                obecComboBoxItemStateChanged(evt);
+            }
+        });
+
+        obecLabel.setText("Obec:");
+
+        castObceLabel.setText("\u010c\u00e1st obce:");
+
+        oblastLabel.setText("Oblast:");
+
+        javax.swing.GroupLayout mainPanelLayout = new javax.swing.GroupLayout(mainPanel);
+        mainPanel.setLayout(mainPanelLayout);
+        mainPanelLayout.setHorizontalGroup(
+            mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(mainPanelLayout.createSequentialGroup()
+                .addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+                    .addComponent(oblastLabel)
+                    .addComponent(obecLabel)
+                    .addComponent(castObceLabel))
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+                    .addComponent(castObceComboBox, 0, 340, Short.MAX_VALUE)
+                    .addComponent(obecComboBox, 0, 340, Short.MAX_VALUE)
+                    .addComponent(oblastComboBox, 0, 340, Short.MAX_VALUE)))
+        );
+        mainPanelLayout.setVerticalGroup(
+            mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(mainPanelLayout.createSequentialGroup()
+                .addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(oblastLabel)
+                    .addComponent(oblastComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE))
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(obecLabel)
+                    .addComponent(obecComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE))
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(castObceLabel)
+                    .addComponent(castObceComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE))
+                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+        );
+        getContentPane().add(mainPanel);
+
+    }// </editor-fold>//GEN-END:initComponents
+
+    /**
+     * Notifies all listeners about
+     *
+     *
+    private void checkSetSelected(ElementWithStreets elem) {
+
+        if (elem == null) return;
+
+        ItemEvent event = new ItemEvent(this,
+                   ItemEvent.DESELECTED, selectedElement, ItemEvent.DESELECTED);
+        
+        for (ItemListener i : listeners)
+            i.itemStateChanged(event);
+
+        selectedElement = elem;
+
+        event = new ItemEvent(this,
+                       ItemEvent.SELECTED, selectedElement, ItemEvent.SELECTED);
+        
+        for (ItemListener i : listeners)
+                i.itemStateChanged(event);
+    }*/
+
+    private void oblastComboBoxItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_oblastComboBoxItemStateChanged
+
+        Region oblast = (Region) oblastComboBox.getSelectedItem();
+        if (oblast == null) return;
+
+        obecComboBox.setModel(new DefaultComboBoxModel(oblast.getViToCis().toArray()));
+        obecComboBox.setEnabled(obecComboBox.getModel().getSize() > 1);
+
+        obecComboBoxItemStateChanged(null);
+    }//GEN-LAST:event_oblastComboBoxItemStateChanged
+
+    private void obecComboBoxItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_obecComboBoxItemStateChanged
+
+        ViToCi obec = (ViToCi) obecComboBox.getSelectedItem();
+        if (obec == null) return;
+
+        castObceComboBox.setModel(new DefaultComboBoxModel(obec.getSuburbs().toArray()));
+        castObceComboBox.setEnabled(castObceComboBox.getModel().getSize() > 1);
+
+        castObceComboBoxItemStateChanged(null);
+    }//GEN-LAST:event_obecComboBoxItemStateChanged
+
+        private void castObceComboBoxItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_castObceComboBoxItemStateChanged
+
+        /*if (castObceComboBox.getSelectedItem() != null)
+            checkSetSelected((ElementWithStreets) castObceComboBox.getSelectedItem());
+
+        else if (obecComboBox.getSelectedItem() != null)
+            checkSetSelected((ElementWithStreets) obecComboBox.getSelectedItem());
+
+        else if (oblastComboBox.getSelectedItem() != null)
+            checkSetSelected((ElementWithStreets) oblastComboBox.getSelectedItem());*/
+
+        if (castObceComboBox.getSelectedItem() != null)
+            selectedElement = ((ElementWithStreets) castObceComboBox.getSelectedItem());
+
+        else if (obecComboBox.getSelectedItem() != null)
+            selectedElement = ((ElementWithStreets) obecComboBox.getSelectedItem());
+
+        else if (oblastComboBox.getSelectedItem() != null)
+            selectedElement = ((ElementWithStreets) oblastComboBox.getSelectedItem());
+        
+        }//GEN-LAST:event_castObceComboBoxItemStateChanged
+
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JComboBox castObceComboBox;
+    private javax.swing.JLabel castObceLabel;
+    private javax.swing.JPanel mainPanel;
+    private javax.swing.JComboBox obecComboBox;
+    private javax.swing.JLabel obecLabel;
+    private javax.swing.JComboBox oblastComboBox;
+    private javax.swing.JLabel oblastLabel;
+    // End of variables declaration//GEN-END:variables
+
+    private class AddressElementRenderer extends DefaultListCellRenderer {
+
+        @Override
+        public Component getListCellRendererComponent(
+                    JList list, Object value, int index,
+                    boolean isSelected, boolean cellHasFocus) {
+
+            Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
+
+            if (value instanceof AddressElement && !(value instanceof Region))
+                setText(((AddressElement) value).getName());
+
+            return c;
+        }
+    }
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/PointManipulatorDialog.form
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/PointManipulatorDialog.form	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/PointManipulatorDialog.form	(revision 15166)
@@ -0,0 +1,173 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<Form version="1.3" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
+  <NonVisualComponents>
+    <Component class="javax.swing.JLabel" name="jLabel4">
+      <Properties>
+        <Property name="text" type="java.lang.String" value="jLabel4"/>
+      </Properties>
+    </Component>
+  </NonVisualComponents>
+  <SyntheticProperties>
+    <SyntheticProperty name="formSizePolicy" type="int" value="2"/>
+  </SyntheticProperties>
+  <AuxValues>
+    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+    <AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,11,0,0,1,-79"/>
+  </AuxValues>
+
+  <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridLayout">
+    <Property name="columns" type="int" value="0"/>
+    <Property name="rows" type="int" value="1"/>
+  </Layout>
+  <SubComponents>
+    <Container class="javax.swing.JPanel" name="mainPanel">
+
+      <Layout>
+        <DimensionLayout dim="0">
+          <Group type="103" groupAlignment="0" attributes="0">
+              <Group type="102" alignment="0" attributes="0">
+                  <Group type="103" groupAlignment="0" attributes="0">
+                      <Component id="jLabel6" alignment="0" min="-2" max="-2" attributes="0"/>
+                      <Component id="jLabel1" min="-2" max="-2" attributes="0"/>
+                      <Component id="jLabel5" min="-2" max="-2" attributes="0"/>
+                  </Group>
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Group type="103" groupAlignment="0" attributes="0">
+                      <Group type="102" alignment="1" attributes="0">
+                          <Component id="locationEdit" pref="215" max="32767" attributes="0"/>
+                          <EmptySpace max="-2" attributes="0"/>
+                          <Component id="changeLocationButton" min="-2" max="-2" attributes="0"/>
+                      </Group>
+                      <Component id="alternateNumberEdit" alignment="0" pref="299" max="32767" attributes="0"/>
+                      <Group type="102" alignment="1" attributes="0">
+                          <Component id="matchesComboBox" pref="174" max="32767" attributes="0"/>
+                          <EmptySpace max="-2" attributes="0"/>
+                          <Component id="ensureConsistencyButton" min="-2" max="-2" attributes="0"/>
+                      </Group>
+                  </Group>
+              </Group>
+              <Component id="jScrollPane1" alignment="1" pref="433" max="32767" attributes="0"/>
+          </Group>
+        </DimensionLayout>
+        <DimensionLayout dim="1">
+          <Group type="103" groupAlignment="0" attributes="0">
+              <Group type="102" alignment="0" attributes="0">
+                  <Group type="103" groupAlignment="3" attributes="0">
+                      <Component id="locationEdit" alignment="3" min="-2" max="-2" attributes="0"/>
+                      <Component id="jLabel5" alignment="3" min="-2" max="-2" attributes="0"/>
+                      <Component id="changeLocationButton" alignment="3" min="-2" max="-2" attributes="0"/>
+                  </Group>
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Group type="103" groupAlignment="3" attributes="0">
+                      <Component id="alternateNumberEdit" alignment="3" min="-2" max="-2" attributes="0"/>
+                      <Component id="jLabel1" alignment="3" min="-2" max="-2" attributes="0"/>
+                  </Group>
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Group type="103" groupAlignment="3" attributes="0">
+                      <Component id="jLabel6" alignment="3" min="-2" max="-2" attributes="0"/>
+                      <Component id="matchesComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
+                      <Component id="ensureConsistencyButton" alignment="3" min="-2" max="-2" attributes="0"/>
+                  </Group>
+                  <EmptySpace pref="186" max="32767" attributes="0"/>
+              </Group>
+              <Group type="102" alignment="0" attributes="0">
+                  <EmptySpace min="-2" pref="92" max="-2" attributes="0"/>
+                  <Component id="jScrollPane1" pref="175" max="32767" attributes="0"/>
+              </Group>
+          </Group>
+        </DimensionLayout>
+      </Layout>
+      <SubComponents>
+        <Component class="javax.swing.JLabel" name="jLabel1">
+          <Properties>
+            <Property name="text" type="java.lang.String" value="&#x10c;&#xed;slo popisn&#xe9;:"/>
+          </Properties>
+        </Component>
+        <Component class="javax.swing.JTextField" name="alternateNumberEdit">
+          <Events>
+            <EventHandler event="keyReleased" listener="java.awt.event.KeyListener" parameters="java.awt.event.KeyEvent" handler="keyReleased"/>
+          </Events>
+        </Component>
+        <Component class="javax.swing.JLabel" name="jLabel5">
+          <Properties>
+            <Property name="text" type="java.lang.String" value="M&#xed;sto:"/>
+          </Properties>
+        </Component>
+        <Component class="javax.swing.JTextField" name="locationEdit">
+          <Properties>
+            <Property name="editable" type="boolean" value="false"/>
+            <Property name="focusable" type="boolean" value="false"/>
+          </Properties>
+        </Component>
+        <Component class="javax.swing.JButton" name="changeLocationButton">
+          <Properties>
+            <Property name="text" type="java.lang.String" value="Zm&#x11b;nit"/>
+            <Property name="focusable" type="boolean" value="false"/>
+          </Properties>
+          <Events>
+            <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="changeLocationButtonActionPerformed"/>
+          </Events>
+        </Component>
+        <Container class="javax.swing.JScrollPane" name="jScrollPane1">
+          <AuxValues>
+            <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
+          </AuxValues>
+
+          <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+          <SubComponents>
+            <Component class="javax.swing.JList" name="proposalList">
+              <Properties>
+                <Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.editors2.ListModelEditor">
+                  <StringArray count="0"/>
+                </Property>
+              </Properties>
+              <Events>
+                <EventHandler event="keyReleased" listener="java.awt.event.KeyListener" parameters="java.awt.event.KeyEvent" handler="proposalListKeyReleased"/>
+              </Events>
+            </Component>
+          </SubComponents>
+        </Container>
+        <Component class="javax.swing.JComboBox" name="matchesComboBox">
+          <Properties>
+            <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
+              <StringArray count="1">
+                <StringItem index="0" value=""/>
+              </StringArray>
+            </Property>
+          </Properties>
+          <Events>
+            <EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="matchChanged"/>
+          </Events>
+        </Component>
+        <Component class="javax.swing.JLabel" name="jLabel6">
+          <Properties>
+            <Property name="text" type="java.lang.String" value="Zaznam v databazi:"/>
+          </Properties>
+        </Component>
+        <Component class="javax.swing.JButton" name="ensureConsistencyButton">
+          <Properties>
+            <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
+              <Connection code="ImageProvider.get(&quot;actions&quot;, &quot;refresh-small.png&quot;)" type="code"/>
+            </Property>
+            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
+              <Connection code="&quot;&quot;" type="code"/>
+            </Property>
+            <Property name="toolTipText" type="java.lang.String" value="Provede nov&#xe9; p&#x159;i&#x159;azen&#xed; prvk&#x16f; mapy na elementy datab&#xe1;ze.&#xa;Touto volbou se zru&#x161;&#xed; v&#x161;echny manu&#xe1;ln&#x11b; vy&#x159;e&#x161;en&#xe9; konflikty."/>
+          </Properties>
+          <Events>
+            <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="ensureConsistencyButtonActionPerformed"/>
+          </Events>
+        </Component>
+      </SubComponents>
+    </Container>
+  </SubComponents>
+</Form>
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/PointManipulatorDialog.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/PointManipulatorDialog.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/PointManipulatorDialog.java	(revision 15166)
@@ -0,0 +1,405 @@
+package org.openstreetmap.josm.plugins.czechaddress.gui;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.util.List;
+import java.util.Map;
+import javax.swing.JList;
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.DefaultListCellRenderer;
+import javax.swing.ImageIcon;
+import javax.swing.Timer;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.gui.ExtendedDialog;
+import org.openstreetmap.josm.plugins.czechaddress.CzechAddressPlugin;
+import org.openstreetmap.josm.plugins.czechaddress.NotNullList;
+import org.openstreetmap.josm.plugins.czechaddress.StatusListener;
+import org.openstreetmap.josm.plugins.czechaddress.addressdatabase.AddressElement;
+import org.openstreetmap.josm.plugins.czechaddress.addressdatabase.House;
+import org.openstreetmap.josm.plugins.czechaddress.intelligence.Match;
+import org.openstreetmap.josm.plugins.czechaddress.intelligence.Reasoner;
+import org.openstreetmap.josm.plugins.czechaddress.proposal.Proposal;
+import org.openstreetmap.josm.plugins.czechaddress.proposal.ProposalContainer;
+import org.openstreetmap.josm.plugins.czechaddress.proposal.ProposalListPainter;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+/**
+ * Dialog for adding/editing an address of a single primitive.
+ *
+ * @author radomir.cernoch@gmail.com
+ */
+public class PointManipulatorDialog extends ExtendedDialog implements StatusListener {
+
+    private Timer  updateMatchesTimes = null;
+    private Action updateMatchesAction;
+    private ProposalContainer proposalContainer;
+
+    /**
+     * Creates and shows the dialog.
+     * @param primitive the primitive, which should be edited by this dialog
+     */
+    public PointManipulatorDialog(OsmPrimitive primitive) {
+
+        super(Main.parent, "Adresní bod",
+                            new String[] { "OK", "Zrušit" }, true);
+        initComponents();
+
+        // Create action for delaying the database query...
+        updateMatchesAction = new AbstractAction() {
+            boolean shouldDraw = false;
+            public void actionPerformed(ActionEvent e) {
+                updateMatches();
+            }
+        };
+
+        // Register for plugin-wide messages.
+        CzechAddressPlugin.addStatusListener(this);
+        updateLocation();
+
+        // A the beginning there are no proposals.
+        proposalContainer = new ProposalContainer(primitive);
+        proposalList.setModel(proposalContainer);
+        proposalList.setCellRenderer(new ProposalListPainter());
+
+        // Init the "match" combobox.
+        matchesComboBox.setModel(new MatchesComboBoxModel());
+        matchesComboBox.setRenderer(new MatchesComboBoxPainter());
+
+        if (primitive.get("addr:alternatenumber") != null) {
+            alternateNumberEdit.setText(primitive.get("addr:alternatenumber"));
+            updateMatches();
+        }
+
+        // And finalize initializing the form.
+        setupDialog(mainPanel, new String[] { "ok.png", "cancel.png" });
+        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
+        setAlwaysOnTop(false);
+
+        // TODO: Why does it always crash if the modality is set in constructor?
+        setModal(false);
+    }
+
+    @Override
+    protected void buttonAction(ActionEvent evt) {
+        super.buttonAction(evt);
+        if (getValue() == 1) {
+            proposalContainer.applyAll();
+
+            Main.ds.setSelected((Node) null); // TODO: This is an ugly hack.
+            Main.ds.setSelected(proposalContainer.getTarget());
+            
+            Reasoner r = CzechAddressPlugin.getReasoner();
+            Match    m = (Match) matchesComboBox.getSelectedItem();
+            if (m != null) {
+                r.overwriteMatch(m.elem, m.prim);
+            }
+        }
+
+        CzechAddressPlugin.removeStatusListener(this);
+    }
+
+    /**
+     * Updates the dialog after a location has changed.
+     */
+    public void updateLocation() {
+        locationEdit.setText(CzechAddressPlugin.getLocation().toString());
+    }
+
+    /**
+     * Updates the combobox with houses that match the current input.
+     */
+    public void updateMatches() {
+        
+        if (proposalContainer.getTarget().deleted) {
+            setVisible(false);
+        }
+        
+        OsmPrimitive prim = this.proposalContainer.getTarget();
+        
+        Map<String,String> backup = prim.keys; 
+        prim.keys = null;
+        prim.put("addr:alternatenumber", alternateNumberEdit.getText());
+        
+        Reasoner r = CzechAddressPlugin.getReasoner();
+        NotNullList<Match> matches = r.getMatchesForPrimitive(prim);
+
+        prim.keys = backup;
+        
+        // TODO: Here we should sort matches according to their quality.
+
+        MatchesComboBoxModel matchesModel =
+                ((MatchesComboBoxModel) matchesComboBox.getModel());
+
+        // Fill the combobox with suitable houses.
+        matchesModel.setMatches(matches);
+        if (matchesModel.getSize() > 0) {
+            matchesComboBox.setSelectedIndex(0);
+            return;
+        }
+
+        // If there are no suitable houses, invent one!
+        House fakeHouse = new House(alternateNumberEdit.getText(), null);
+        fakeHouse.setParent(CzechAddressPlugin.getLocation());
+        proposalContainer.setProposals(
+                fakeHouse.getDiff(proposalContainer.getTarget()));
+    }
+
+    public void pluginStatusChanged(int message) {
+
+        // If location changes, we block the dialog until reasoning is done.
+        if (message == MESSAGE_LOCATION_CHANGED) {
+            updateLocation();
+            mainPanel.setEnabled(false);
+
+        // When reasoning is done, dialog gets enabled and new proposals are added.
+        } else if (message == MESSAGE_MATCHES_CHANGED) {
+            updateMatches();
+            mainPanel.setEnabled(true);
+        }
+    }
+
+    /**
+     * This method is called from within the constructor to
+     * initialize the form.
+     * 
+     * <p><b>WARNING:</b> Do NOT modify this code. The content of this method is
+     * always regenerated by the Form Editor.</p>
+     */
+    @SuppressWarnings("unchecked")
+    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+    private void initComponents() {
+
+        jLabel4 = new javax.swing.JLabel();
+        mainPanel = new javax.swing.JPanel();
+        jLabel1 = new javax.swing.JLabel();
+        alternateNumberEdit = new javax.swing.JTextField();
+        jLabel5 = new javax.swing.JLabel();
+        locationEdit = new javax.swing.JTextField();
+        changeLocationButton = new javax.swing.JButton();
+        jScrollPane1 = new javax.swing.JScrollPane();
+        proposalList = new javax.swing.JList();
+        matchesComboBox = new javax.swing.JComboBox();
+        jLabel6 = new javax.swing.JLabel();
+        ensureConsistencyButton = new javax.swing.JButton();
+
+        jLabel4.setText("jLabel4");
+
+        getContentPane().setLayout(new java.awt.GridLayout(1, 0));
+
+        jLabel1.setText("Číslo popisné:");
+
+        alternateNumberEdit.addKeyListener(new java.awt.event.KeyAdapter() {
+            public void keyReleased(java.awt.event.KeyEvent evt) {
+                PointManipulatorDialog.this.keyReleased(evt);
+            }
+        });
+
+        jLabel5.setText("Místo:");
+
+        locationEdit.setEditable(false);
+        locationEdit.setFocusable(false);
+
+        changeLocationButton.setText("Změnit");
+        changeLocationButton.setFocusable(false);
+        changeLocationButton.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                changeLocationButtonActionPerformed(evt);
+            }
+        });
+
+        proposalList.addKeyListener(new java.awt.event.KeyAdapter() {
+            public void keyReleased(java.awt.event.KeyEvent evt) {
+                proposalListKeyReleased(evt);
+            }
+        });
+        jScrollPane1.setViewportView(proposalList);
+
+        matchesComboBox.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "" }));
+        matchesComboBox.addItemListener(new java.awt.event.ItemListener() {
+            public void itemStateChanged(java.awt.event.ItemEvent evt) {
+                matchChanged(evt);
+            }
+        });
+
+        jLabel6.setText("Zaznam v databazi:");
+
+        ensureConsistencyButton.setIcon(ImageProvider.get("actions", "refresh-small.png"));
+        ensureConsistencyButton.setText("");
+        ensureConsistencyButton.setToolTipText("Provede nové přiřazení prvků mapy na elementy databáze.\nTouto volbou se zruší všechny manuálně vyřešené konflikty."); // NOI18N
+        ensureConsistencyButton.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                ensureConsistencyButtonActionPerformed(evt);
+            }
+        });
+
+        javax.swing.GroupLayout mainPanelLayout = new javax.swing.GroupLayout(mainPanel);
+        mainPanel.setLayout(mainPanelLayout);
+        mainPanelLayout.setHorizontalGroup(
+            mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(mainPanelLayout.createSequentialGroup()
+                .addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                    .addComponent(jLabel6)
+                    .addComponent(jLabel1)
+                    .addComponent(jLabel5))
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, mainPanelLayout.createSequentialGroup()
+                        .addComponent(locationEdit, javax.swing.GroupLayout.DEFAULT_SIZE, 249, Short.MAX_VALUE)
+                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                        .addComponent(changeLocationButton))
+                    .addComponent(alternateNumberEdit, javax.swing.GroupLayout.DEFAULT_SIZE, 306, Short.MAX_VALUE)
+                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, mainPanelLayout.createSequentialGroup()
+                        .addComponent(matchesComboBox, 0, 208, Short.MAX_VALUE)
+                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                        .addComponent(ensureConsistencyButton))))
+            .addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                .addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 433, Short.MAX_VALUE))
+        );
+        mainPanelLayout.setVerticalGroup(
+            mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(mainPanelLayout.createSequentialGroup()
+                .addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(locationEdit, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                    .addComponent(jLabel5)
+                    .addComponent(changeLocationButton))
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(alternateNumberEdit, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                    .addComponent(jLabel1))
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(jLabel6)
+                    .addComponent(matchesComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                    .addComponent(ensureConsistencyButton))
+                .addContainerGap(179, Short.MAX_VALUE))
+            .addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                .addGroup(mainPanelLayout.createSequentialGroup()
+                    .addGap(92, 92, 92)
+                    .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 175, Short.MAX_VALUE)))
+        );
+
+        getContentPane().add(mainPanel);
+    }// </editor-fold>//GEN-END:initComponents
+
+    private void changeLocationButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_changeLocationButtonActionPerformed
+        CzechAddressPlugin.changeLocation();
+    }//GEN-LAST:event_changeLocationButtonActionPerformed
+
+    private void keyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_keyReleased
+        if (updateMatchesTimes != null)
+            updateMatchesTimes.stop();
+        
+        updateMatchesTimes = new Timer(300, updateMatchesAction);
+        updateMatchesTimes.setRepeats(false);
+        updateMatchesTimes.start();
+    }//GEN-LAST:event_keyReleased
+
+    private void proposalListKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_proposalListKeyReleased
+        if (evt.getKeyCode() == KeyEvent.VK_DELETE) {
+            for (Object o : proposalList.getSelectedValues())
+                proposalContainer.removeProposal((Proposal) o);
+        }
+    }//GEN-LAST:event_proposalListKeyReleased
+
+    private void matchChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_matchChanged
+
+        if (matchesComboBox.getSelectedItem() == null) return;        
+        Match match = (Match) matchesComboBox.getSelectedItem();
+
+        proposalContainer.setProposals(match.elem.getDiff(
+                                                proposalContainer.getTarget()));
+    }//GEN-LAST:event_matchChanged
+
+    private void ensureConsistencyButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_ensureConsistencyButtonActionPerformed
+        CzechAddressPlugin.getReasoner().ensureConsistency();
+    }//GEN-LAST:event_ensureConsistencyButtonActionPerformed
+
+
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JTextField alternateNumberEdit;
+    private javax.swing.JButton changeLocationButton;
+    private javax.swing.JButton ensureConsistencyButton;
+    private javax.swing.JLabel jLabel1;
+    private javax.swing.JLabel jLabel4;
+    private javax.swing.JLabel jLabel5;
+    private javax.swing.JLabel jLabel6;
+    private javax.swing.JScrollPane jScrollPane1;
+    private javax.swing.JTextField locationEdit;
+    private javax.swing.JPanel mainPanel;
+    private javax.swing.JComboBox matchesComboBox;
+    private javax.swing.JList proposalList;
+    // End of variables declaration//GEN-END:variables
+
+    /**
+     * Painter for adding icons to the {@code matchesComboBox}.
+     */
+    private class MatchesComboBoxPainter extends DefaultListCellRenderer {
+
+        ImageIcon envelopeNormIcon = ImageProvider.get("envelope-closed-small.png");
+        ImageIcon envelopeStarIcon = ImageProvider.get("envelope-closed-star-small.png");
+        ImageIcon envelopeExclIcon = ImageProvider.get("envelope-closed-exclamation-small.png");
+
+        @Override
+        public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
+            Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
+
+            Reasoner r = CzechAddressPlugin.getReasoner();
+            setIcon(null);
+
+            if (value instanceof Match) {
+                Match match = (Match) value;
+
+                setText(AddressElement.getName(match.elem));
+
+                if (match.elem instanceof House) {
+                    setIcon(envelopeStarIcon);
+                    if ( r.getConflicts(match.elem) != null )
+                        setIcon(envelopeExclIcon);
+                    else if ( r.translate(match.elem) != null)
+                        setIcon(envelopeNormIcon);
+                }
+            }
+
+            return c;
+        }
+    }
+
+    /**
+     * Container for all Houses, which match the given 'alternatenumber'.
+     */
+    private class MatchesComboBoxModel extends HalfCookedComboBoxModel {
+
+        private List<Match> matches = null;
+        int selectedIndex = -1;
+
+        public void setMatches(List<Match> matches) {
+            this.matches = matches;
+            selectedIndex = -1;
+            notifyAllListeners();
+        }
+
+        public void setSelectedItem(Object anItem) {
+            if (matches == null) return;
+            selectedIndex = matches.indexOf(anItem);
+        }
+
+        public Object getSelectedItem() {
+            return getElementAt(selectedIndex);
+        }
+
+        public int getSize() {
+            if (matches == null) return 0;
+            return matches.size();
+        }
+
+        public Object getElementAt(int index) {
+            if (matches == null) return null;
+            if ((index < 0) || (index >= matches.size())) return null;
+            return matches.get(index);
+        }
+    }
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/UniversalTreeRenderer.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/UniversalTreeRenderer.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/gui/UniversalTreeRenderer.java	(revision 15166)
@@ -0,0 +1,90 @@
+package org.openstreetmap.josm.plugins.czechaddress.gui;
+
+import java.awt.Component;
+import javax.swing.ImageIcon;
+import javax.swing.JTree;
+import javax.swing.tree.DefaultTreeCellRenderer;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.plugins.czechaddress.CzechAddressPlugin;
+import org.openstreetmap.josm.plugins.czechaddress.addressdatabase.AddressElement;
+import org.openstreetmap.josm.plugins.czechaddress.addressdatabase.House;
+import org.openstreetmap.josm.plugins.czechaddress.intelligence.Reasoner;
+import org.openstreetmap.josm.plugins.czechaddress.proposal.AddKeyValueProposal;
+import org.openstreetmap.josm.plugins.czechaddress.proposal.KeyValueChangeProposal;
+import org.openstreetmap.josm.plugins.czechaddress.proposal.ProposalContainer;
+import org.openstreetmap.josm.plugins.czechaddress.proposal.RemoveKeyProposal;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+/**
+ * Renderer for rendering trees with {@link OsmPrimitive}s and
+ * {@link AddressElement}s.
+ *
+ * @author Radomír Černoch, radomir.cernoch@gmail.com
+ */
+public class UniversalTreeRenderer extends DefaultTreeCellRenderer {
+    static ImageIcon iconAdd    = ImageProvider.get("actions", "add.png");
+    static ImageIcon iconEdit   = ImageProvider.get("actions", "edit.png");
+    static ImageIcon iconRemove = ImageProvider.get("actions", "remove.png");
+
+    static ImageIcon nodeIcon      = ImageProvider.get("Mf_node.png");
+    static ImageIcon wayIcon       = ImageProvider.get("Mf_way.png");
+    static ImageIcon closedWayIcon = ImageProvider.get("Mf_closedway.png");
+    static ImageIcon relationIcon  = ImageProvider.get("Mf_relation.png");
+    
+    static ImageIcon envelopeNormIcon = ImageProvider.get("envelope-closed-small.png");
+    static ImageIcon envelopeStarIcon = ImageProvider.get("envelope-closed-star-small.png");
+    static ImageIcon envelopeExclIcon = ImageProvider.get("envelope-closed-exclamation-small.png");
+
+    @Override
+    public Component getTreeCellRendererComponent(
+            JTree tree, Object value, boolean sel,
+            boolean expanded, boolean leaf, int row, boolean hasFocus) {
+
+        Component c = super.getTreeCellRendererComponent(tree, value, sel,
+                expanded, leaf, row, hasFocus);
+
+        /*if (plainFont == null) plainFont = getFont().deriveFont(Font.PLAIN);
+        if ( boldFont == null)  boldFont = getFont().deriveFont(Font.BOLD);*/
+        Reasoner r = CzechAddressPlugin.getReasoner();
+
+
+        if (value instanceof ProposalContainer) {
+            value = ((ProposalContainer) value).getTarget();
+        }
+
+        if ((value instanceof AddressElement) || (value instanceof OsmPrimitive)) {
+            setText(AddressElement.getName(value));
+        }
+
+             if (value instanceof AddKeyValueProposal)    setIcon(iconAdd);
+        else if (value instanceof KeyValueChangeProposal) setIcon(iconEdit);
+        else if (value instanceof RemoveKeyProposal)      setIcon(iconRemove);
+
+
+        if (value instanceof House) {
+            House house = (House) value;
+
+            setIcon(envelopeNormIcon);
+            if ( r.getConflicts(house) != null )
+                setIcon(envelopeExclIcon);
+            else if ( r.translate(house) == null)
+                setIcon(envelopeStarIcon);
+    
+        } else if (value instanceof Node) {
+            setIcon(nodeIcon);
+        } else if (value instanceof Relation) {
+            setIcon(relationIcon);
+        } else if (value instanceof Way) {
+            if (((Way) value).isClosed()) {
+                setIcon(closedWayIcon);
+            } else {
+                setIcon(wayIcon);
+            }
+        }
+        
+        return c;
+    }
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/intelligence/Match.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/intelligence/Match.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/intelligence/Match.java	(revision 15166)
@@ -0,0 +1,87 @@
+package org.openstreetmap.josm.plugins.czechaddress.intelligence;
+
+import java.util.List;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.plugins.czechaddress.addressdatabase.AddressElement;
+import org.openstreetmap.josm.plugins.czechaddress.proposal.Proposal;
+
+/**
+ * Match is a relation between {@link OsmPrimitive} and {@link AddressElement}.
+ *
+ * @author Radomír Cernoch radomir.cernoch@gmail.com
+ */
+public class Match {
+    
+    public static final int MATCH_OVERWRITE = 4;
+    public static final int MATCH_ROCKSOLID = 3;
+    public static final int MATCH_PARTIAL   = 2;
+    public static final int MATCH_CONFLICT  = 1;
+    public static final int MATCH_NOMATCH   = 0;
+    
+    public int quality;
+    public OsmPrimitive prim;
+    public AddressElement elem;
+
+    public String checkSum;
+
+    public Match(AddressElement element, OsmPrimitive primitive,
+                 int qualityFactor) {
+        
+        assert primitive != null;
+        assert element != null;
+        assert qualityFactor <= MATCH_OVERWRITE;
+        assert qualityFactor > MATCH_NOMATCH
+             : "MATCH_NOMATCH represents no relation."
+             + "It's pointless to waste memory for it.";
+
+        assert !primitive.deleted;
+
+        prim = primitive;
+        elem = element;
+        quality = qualityFactor;
+
+        checkSum = toString();
+    }
+
+    public List<Proposal> getDiff() {
+        return this.elem.getDiff(this.prim);
+    }
+
+    public static Match createMatch(AddressElement element,
+                                    OsmPrimitive primitive) {
+
+        int quality = element.getMatchQuality(primitive);
+        if (quality > MATCH_NOMATCH)
+            return new Match(element, primitive, quality);
+
+        return null;
+    }
+
+    public boolean qualityChanged() {
+        int newQuality
+        = prim.deleted ? MATCH_NOMATCH
+                       : quality == MATCH_OVERWRITE ? MATCH_OVERWRITE
+                                                    : elem.getMatchQuality(prim);
+        if (prim instanceof Node) {
+            Node node = (Node) prim;
+            if (node.coor == null || node.eastNorth == null) {
+                newQuality = MATCH_NOMATCH;
+                System.out.println("Suspicious node, ignoring it: " + AddressElement.getName(node));
+            }
+        }
+            
+        
+        boolean difference = newQuality != quality;
+        quality = newQuality;
+
+        return difference;
+    }
+
+    @Override
+    public String toString() {
+        return "{Match: q=" + String.valueOf(quality)
+                   + "; elem='" + elem.toString()
+                   +"'; prim='" + AddressElement.getName(prim) + "'}";
+    }
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/intelligence/Reasoner.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/intelligence/Reasoner.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/intelligence/Reasoner.java	(revision 15166)
@@ -0,0 +1,691 @@
+package org.openstreetmap.josm.plugins.czechaddress.intelligence;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.plugins.czechaddress.CzechAddressPlugin;
+import org.openstreetmap.josm.plugins.czechaddress.NotNullList;
+import org.openstreetmap.josm.plugins.czechaddress.StatusListener;
+import org.openstreetmap.josm.plugins.czechaddress.addressdatabase.AddressElement;
+import org.openstreetmap.josm.plugins.czechaddress.addressdatabase.House;
+import org.openstreetmap.josm.plugins.czechaddress.proposal.ProposalContainer;
+import org.openstreetmap.josm.plugins.czechaddress.proposal.ProposalDatabase;
+
+/**
+ * Intended to concentrate all intelligence of
+ * {@link House}-{@link OsmPrimitive} matching.
+ *
+ * <p>HouseReasoner holds the relations between House and OsmPrimitive
+ * and also tries to keep it consistent with the state of the map.
+ * Firstly it is initialised by {@code initReasoner()} and subsequently and
+ * change done to the map should be reflected in HouseReasoner by calling
+ * {@code addPrimitive} or {@code removePrimitive}.</p>
+ *
+ * <p><b>NOTE:</b> Currently there is no known way of adding a hook into JOSM
+ * to detect changed or deleted elements. Therefore every call of
+ * {@code ensureConsistency()} checks for deleted elements, which is rather
+ * inefficient. Moreover there is a listener for selectionChanged, which
+ * checks deselcted items for their change. Again, inefficient.</p>
+ *
+ * @author Radomír Černoch radomir.cernoch@gmail.com
+ */
+public class Reasoner {
+
+    /** A database of all matches, which are not in a conflict. */
+    private NotNullList<Match> matches = new NotNullList<Match>();
+
+    /** A list of {@link Match}es, which are in a conflict. */
+    private NotNullList<Match> conflicts = new NotNullList<Match>();
+
+    /** A list of {@link House}s, which have not yet been matched to any
+     * {@link OsmPrimitive} without a conflict. */
+    private ArrayList<AddressElement> elemPool = new ArrayList<AddressElement>();
+
+    /** A list of {@link OsmPrimitive}s, for which there was no suitable match. */
+    private List<OsmPrimitive> notMatchable = new ArrayList<OsmPrimitive>();
+
+    private HashMap<OsmPrimitive,   Match> primMatchHashIndex
+      = new HashMap<OsmPrimitive,   Match>();
+    private HashMap<AddressElement, Match> elemMatchHashIndex
+      = new HashMap<AddressElement, Match>();
+
+    private List<OsmPrimitive>   primConflictListIndex = new ArrayList<OsmPrimitive>();
+    private List<AddressElement> elemConflictListIndex = new ArrayList<AddressElement>();
+    private HashMap<OsmPrimitive,   List<Match>> primConflictHashIndex
+      = new HashMap<OsmPrimitive,   List<Match>>();
+    private HashMap<AddressElement, List<Match>> elemConflictHashIndex
+      = new HashMap<AddressElement, List<Match>>();
+
+
+    /**
+     * Default constructor, which initializes the pool of
+     * {@link AddressElement}s to be used for matching.
+     */
+    public Reasoner(ArrayList<AddressElement> elementPool) {
+        elemPool = elementPool;
+    }    
+    
+    /**
+     * Adds a single new primitive to the database, finds corresponding
+     * {@link House} for it and seeks for potential conflicts.
+     * 
+     * <p>If you intend to add a list of new primitives, {@link addPrimitives}
+     * will be faster, because conflicts are traced after adding all the
+     * primitives and not after every single one.</p>
+     *
+     * <p>Moreover there is one substantial difference discussed in
+     * {@link addPrimitives()}.</p>
+     *
+     * @param newPrimitive new primitive to be added
+     */
+    public synchronized void addPrimitive(OsmPrimitive newPrimitive) {
+
+        int firstNewIndex = matches.size();
+
+        matchPrimitive(newPrimitive);
+        
+        ensureConsistency(firstNewIndex);
+    }
+
+    /**
+     * Adds new primitives to the database, finds suitable {@link House}s for
+     * them and afterwards seeks for potential conflicts.
+     *
+     * <p><b>NOTE:</b> Adding primitives through {@code addPrimitives()} has
+     * different effect from adding primitives one by one through
+     * {@code addPrimitive()}.</p>
+     * 
+     * <p>The preffered way of handling this is to add a large bunch of elements
+     * from the database via {@code addPrimitives()}, because the possible
+     * conflicts are between all possible element-primitive combinations.
+     * For adding single node by user, {@code addPrimitives()} is the preferred
+     * way, because immediatelly after the matching the best suitable element
+     * is removed from the element pool and it cannot be matched to any other
+     * primitive.</p>
+     */
+    public synchronized void addPrimitives(Collection<OsmPrimitive> newPrimitives) {
+
+        int firstNewIndex = matches.size();
+
+        for (OsmPrimitive primitive : newPrimitives)
+            matchPrimitive(primitive);
+
+        ensureConsistency(firstNewIndex);
+    }
+
+    /**
+     * Removes the given primitive from the database and cancels all conflicts
+     * which can be solved by this removal.
+     */
+    public synchronized void removePrimitive(OsmPrimitive primitive) {
+
+        // TODO: Implement the removal of a primitive
+    }
+
+    /**
+     * Forcibly matches given house to given primitive.
+     *
+     * <p>This can be useful in cases, where the user manually resolves
+     * a conflict by stating that this particular combination of
+     * house-primitive it the correct one.</p>
+     *
+     * <p>The quality is set to {@link Match}{@code .MATCH_OVERWRITE}.</p>
+     */
+    public synchronized void overwriteMatch(AddressElement elem, OsmPrimitive prim) {
+
+        int firstNewIndex = matches.size();
+
+        matches.add(new Match(elem, prim, Match.MATCH_OVERWRITE));
+        matchesDirty = true;
+
+        reconsider(elem);
+        reconsider(prim);
+
+        ensureConsistency(firstNewIndex);
+    }
+
+    /**
+     * Returns all elements available for matching.
+     */
+    public List<AddressElement> getElementPool() {
+        return elemPool;
+    }
+
+    /** Returns all primitives, which have not been assigned any element during
+     * the matching. */
+    public List<OsmPrimitive> getNotMatchable() {
+        return notMatchable;
+    }
+
+    public List<Match> getAllMatches() {
+
+        return matches;
+    }
+
+    public Match findMatch(OsmPrimitive prim) {
+        return primMatchHashIndex.get(prim);
+    }
+
+    public Match findMatch(AddressElement elem) {
+        return primMatchHashIndex.get(elem);
+    }
+
+    /**
+     * Finds given primitive in list of {@code  matches} and returns
+     * the corresponding element.
+     * 
+     * <p>If the primitive is not found, {@code null} is returned.</p>
+     */
+    public AddressElement translate(OsmPrimitive prim) {
+        Match m = primMatchHashIndex.get(prim);
+        if (m == null) return null;
+        return m.elem;
+    }
+
+    /**
+     * Finds given element in list of {@code matches} and returns the corresponding
+     * primitive.
+     *
+     * <p>If the element is not found, {@code null} is returned.</p>
+     */
+    public OsmPrimitive translate(AddressElement elem) {
+        Match m = elemMatchHashIndex.get(elem);
+        if (m == null) return null;
+        return m.prim;
+    }
+
+    /**
+     * Returns a list all conflicts corresponding to the given {@link AddressElement}.
+     */
+    public List<Match> getConflicts(AddressElement elem) {
+        return elemConflictHashIndex.get(elem);
+    }
+
+    /**
+     * Returns a list all conflicts corresponding to the given {@link OsmPrimitive}.
+     */
+    public List<Match> getConflicts(OsmPrimitive prim) {
+        return primConflictHashIndex.get(prim);
+    }
+
+    /**
+     * Returns a sorted list of all elements in conflict.
+     */
+    public List<AddressElement> getElementsInConflict() {
+        return elemConflictListIndex;
+    }
+
+    /**
+     * Returns a sorted list of all primitives in conflict.
+     */
+    public List<OsmPrimitive> getPrimitivesInConflict() {
+        return primConflictListIndex;
+    }
+
+    /**
+     * Returns the list of all conflicts.
+     */
+    public List<Match> getAllConflicts() {
+        return conflicts;
+    }
+
+    /**
+     * Goes through all matches and returns proposals for changing
+     * every primitive (if it differs from the matched element).
+     */
+    public ProposalDatabase getProposals() {
+
+        ProposalDatabase proposals = new ProposalDatabase();
+
+        for (Match match : matches) {
+            
+            ProposalContainer proposalContainer
+                    = new ProposalContainer(match.prim);
+            
+            proposalContainer.addProposals(match.getDiff());
+
+            if (proposalContainer.getProposals().size() > 0)
+                proposals.addContainer(proposalContainer);
+        }
+
+        return proposals;
+    }
+
+    /**
+     * Finds suitable {@code Match}es in the pool of {@link AddressElement}s.
+     *
+     * @param primitive the primitive to be matched with elements of
+     * {@code elemPool}.
+     * @return the list of all matches, whose <i>quality</i> &gt;
+     * {@link Match}{@code .MATCH_NOMATCH}.
+     */
+    public NotNullList<Match> getMatchesForPrimitive(OsmPrimitive primitive) {
+
+        NotNullList<Match> result = new NotNullList<Match>();
+        
+        for (AddressElement elem : elemPool)
+            result.add(Match.createMatch(elem, primitive));
+        return result;
+    }
+
+    /**
+     * Method for adding matches for a single primitive into the resoner.
+     *
+     * <p>This method uses {@code getMatchesForPrimitive()} for getting the
+     * list of suitable matches for the given primitive. Then it selects
+     * <u>all</u> matches with the highest {@code quality} and adds them
+     * into the {@code matches} list.
+     */
+    protected void matchPrimitive(OsmPrimitive prim) {
+        boolean assertions = false;
+        assert  assertions = true;
+
+        if (prim.deleted) return;
+
+        NotNullList<Match> suitable = getMatchesForPrimitive(prim);
+        NotNullList<Match> toDelete = new NotNullList<Match>(suitable.size());
+
+        for (Match match1 : suitable)
+            for (Match match2 : suitable)
+                if (   match1        != match2
+                    && match1.quality > match2.quality) {
+                    toDelete.add(match2);
+                    if (assertions)
+                        System.out.println("Reasoner: Dominated match: " + match2.toString());
+                }
+
+        suitable.removeAll(toDelete);
+
+        // Make sure we reconsider all elements.
+        for (Match match : matches) {
+            reconsider(match.elem);
+            reconsider(match.prim);
+        }
+
+        if (suitable.size() > 0) {
+            matches.addAll(suitable);
+            matchesDirty = true;
+        } else
+            notMatchable.add(prim);
+    }
+
+//==============================================================================
+//  MESSAGE HANDLING SYSTEM
+//==============================================================================
+
+    /**
+     * Should be true whenever {@code matches} have changed, but the
+     * {@link StatusListener}{@code .MESSAGE_MATCHES_CHANGED} message has
+     * not yet been sent.
+     */
+    protected boolean matchesDirty = false;
+
+    /**
+     * Should be true whenever {@code conflicts} have changed, but the
+     * {@link StatusListener}{@code .MESSAGE_CONFLICT_CHANGED} message has
+     * not yet been sent.
+     */
+    protected boolean conflictsDirty = false;
+
+    /**
+     * Broadcasts information about the changes of reasoner status.
+     *
+     * <p>If the {@code matchesDirty} or {@code conflictsDirty} flag is
+     * {@link true}, this method informs all listeners about the change
+     * of plugin's status.</p>
+     */
+    protected void handleDirt() {
+
+        if (matchesDirty || conflictsDirty)
+            regenerateIndexes();
+
+        if (matchesDirty) CzechAddressPlugin.broadcastStatusChanged(
+                StatusListener.MESSAGE_MATCHES_CHANGED);
+
+        if (conflictsDirty) CzechAddressPlugin.broadcastStatusChanged(
+                StatusListener.MESSAGE_CONFLICT_CHANGED);
+
+        matchesDirty = conflictsDirty = false;
+    }
+
+//==============================================================================
+//  CONSISTENCY CHECKING
+//==============================================================================
+
+    /**
+     * Should be called after modifying <b>anything</b> to put reasoner into
+     * consistent state.
+     *
+     * <p>This method should be called at the end of every public method,
+     * which changes anything in the reasoner.</p>
+     */
+    public void ensureConsistency() {
+        ensureConsistency(0);
+    }
+
+    /**
+     * Should be called after modifying <b>anything</b> to put reasoner into
+     * consistent state.
+     *
+     * <p>This method should be called at the end of every public method,
+     * which changes anything in the reasoner.</p>
+     *
+     * <p>Checking every match with every match is computationally inefficient.
+     * Therefore by specifying {@code startElementIndex > 0}, we can
+     * reduce this cost by checking <i>every match</i> with <i>every match,
+     * whose index is greater than {@code startElementIndex}.</p>
+     */
+    public void ensureConsistency(int startElementIndex) {
+        startElementIndex = 0;
+
+        NotNullList<Match> toDel = new NotNullList<Match>(10);
+        for (Match match1 : matches) for (Match match2 : matches) {
+
+            if (match1 == match2) continue;
+
+            if (match1.prim == match2.prim && match1.quality > match2.quality) {
+                System.out.println("Reasoner: Redundancy clean: " + match2);
+                toDel.add(match2);
+                matchesDirty = true;
+            }
+
+            if (match1.prim == match2.prim && match1.quality >= match2.quality
+             && match1.elem == match2.elem && !toDel.contains(match1)) {
+                System.out.println("Reasoner: Hyper redundancy: " + match2);
+                toDel.add(match2);
+                matchesDirty = true;
+            }
+        }
+        matches.removeAll(toDel);
+
+        toDel.clear();
+        for (Match conflict1 : conflicts) for (Match conflict2 : conflicts) {
+
+            if (conflict1 == conflict2) continue;
+
+            if (conflict1.prim == conflict2.prim
+             && conflict1.elem == conflict2.elem && !toDel.contains(conflict1)) {
+                System.out.println("Reasoner: Confl redundancy: " + conflict2);
+                toDel.add(conflict2);
+                conflictsDirty = true;
+            }
+        }
+        conflicts.removeAll(toDel);
+
+        for (Match match : matches)
+            for (Match conflict : conflicts) {
+                assert match      != conflict;
+                assert match.prim != conflict.prim;
+            }
+
+
+
+        if (handleDeletedPrimitivesSlowButSafe())
+            startElementIndex = 0;
+        handleInconsistentMatches(startElementIndex);
+        handleDirt();
+
+        CzechAddressPlugin.broadcastStatusChanged(
+                                    StatusListener.MESSAGE_REASONER_REASONED);
+    }
+
+    /**
+     * Finds conflicts in the {@code matches} list and moves them to
+     * {@code conflicts} list.
+     */
+    protected void handleInconsistentMatches(int startElementIndex) {
+        boolean assertions = false;
+        assert  assertions = true;
+
+
+        // Move all conflicting matches into 'conflicts' array.
+        int pos1 = 0;
+        while (pos1 < matches.size()) {
+
+            int pos2 = Math.max(pos1 + 1, startElementIndex);
+            while (pos2 < matches.size()) {
+
+                Match item1 = matches.get(pos1);
+                Match item2 = matches.get(pos2);
+
+                if ((item1.elem == item2.elem) || (item1.prim == item2.prim)) {
+
+                    if (assertions) {
+                        System.out.println("1. match in conflict: " + item2);
+                        System.out.println("2. match in conflict: " + item1);
+                    }
+
+                    if (item1.quality >= item2.quality) {
+                        if (assertions)
+                            System.out.println("1. match moved to 'conflicts'.");
+                        matches.remove(pos2);
+                        conflicts.add(item2);
+                        matchesDirty = conflictsDirty = true;
+                        pos2--;
+                    }
+
+                    if (item1.quality <= item2.quality) {
+                        if (assertions) {
+                            System.out.println("2. match moved to 'conflicts'.");
+                            System.out.println("----------------------------------------------------------------------");
+                        }
+                        matches.remove(pos1);
+                        conflicts.add(item1);
+                        matchesDirty = conflictsDirty = true;
+                        pos1--;
+                        break;
+                    }
+
+                    if (assertions)
+                        System.out.println("----------------------------------------------------------------------");
+                }
+
+                pos2++;
+            }
+            pos1++;
+        }
+    }
+
+    /**
+     * Seeks and handles primitives, which have been deleted.
+     *
+     * <p>If a primitive has been deleted, the first immediate action is to
+     * remove it from the {@code matches} and {@code conflicts} lists.
+     * However there might be another conflict, which was caused by the
+     * deleted primitive. This method finds such conflicts and
+     * moves them back to {@code matches} list to
+     * be reconsideres later.</p>
+     */
+    protected boolean handleDeletedPrimitives() {
+
+        boolean somethingChanged = false;
+        NotNullList<OsmPrimitive> blockers = new NotNullList<OsmPrimitive>();
+
+        // Firstly create a list of all primitives, which were deleted.
+        for (Match match : matches)
+            if (match.prim.deleted)
+                blockers.add(match.prim);
+
+        for (Match conflict : conflicts)
+            if (conflict.prim.deleted)
+                blockers.add(conflict.prim);
+
+        // Now for every deleted primitive...
+        int i=0;
+        while (i < blockers.size()) {
+            OsmPrimitive blocker = blockers.get(i);
+
+            // ... remove its' entries in the 'matches' ...
+            if (primMatchHashIndex.get(blocker) != null) {
+                Match toRemove = primMatchHashIndex.get(blocker);
+                matches.remove(toRemove);
+                primMatchHashIndex.remove(toRemove.prim);
+                elemMatchHashIndex.remove(toRemove.elem);
+                somethingChanged = matchesDirty = true;
+            }
+
+            // ... and reconsider all 'conflicts', which may have been caused
+            // by the deleted primitive. We must do it recursively...
+
+            // Every conflict, which has the 'blocker' as a primitive
+            if (primConflictHashIndex.get(blocker) != null) {
+                for (Match match : primConflictHashIndex.get(blocker)) {
+                    // Find the correspoding element and find all primitives,
+                    // which are mapped to this element.
+                    if (elemConflictHashIndex.get(match.elem) != null) {
+                        for (Match novy : elemConflictHashIndex.get(match.elem)) {
+                            // If this primitive has not yet been handled, do it!
+                            if (!blockers.contains(novy.prim))
+                                blockers.add(novy.prim);
+                            // And move the conflict back to 'matches' for reconsideration.
+                            if (!novy.prim.deleted && !matches.contains(novy)) {
+                                matches.add(novy);
+                                somethingChanged = matchesDirty = true;
+                            }
+                        }
+                        // Finally remove the reconsidered conflicts...
+                        conflicts.removeAll(elemConflictHashIndex.get(match.elem));
+                        elemConflictHashIndex.remove(match.elem);
+                        elemConflictListIndex.remove(match.elem);
+                        somethingChanged = conflictsDirty = true;
+                    }
+                    // [move the conflict back to 'matches' for reconsideration]
+                    if (!match.prim.deleted && !matches.contains(match)) {
+                        matches.add(match);
+                        somethingChanged = matchesDirty = true;
+                    }
+                }
+                // ...and once again remove reconsidered conflicts.
+                conflicts.removeAll(primConflictHashIndex.get(blocker));
+                primConflictHashIndex.remove(blocker);
+                primConflictListIndex.remove(blocker);
+                somethingChanged = conflictsDirty = true;
+            }
+            i++;
+        }
+
+        return somethingChanged;
+    }
+
+    /**
+     * Seeks and handles primitives, which have been deleted.
+     *
+     * <p>Slower, but safer counterpart of {@code handleDeletedPrimitives))}.</p>
+     */
+    protected boolean handleDeletedPrimitivesSlowButSafe() {
+
+        boolean fire = false;
+
+        for (Match match : matches)
+            fire |= match.qualityChanged();
+
+        for (Match conflict : conflicts)
+            fire |= conflict.qualityChanged();
+
+        if (fire) {
+            matches.addAll(conflicts);
+            conflicts.clear();
+            matchesDirty = conflictsDirty = true;
+
+            int i=0;
+            while (i<matches.size()) {
+                Match match = matches.get(i);
+                if (match.quality <= Match.MATCH_NOMATCH) {
+                    System.out.println("Reasoner: Deleting " + matches.get(i));
+                    matches.remove(i);
+
+                    assert fire;
+                    assert !matches.contains(match);
+                } else
+                    i++;
+            }
+        }
+
+        return fire;
+    }
+
+    protected void reconsider(OsmPrimitive prim) {
+
+        List<Match> reconsider;
+
+        reconsider = getConflicts(prim);
+        if (reconsider != null) {
+            matches.addAll(reconsider);
+            conflicts.removeAll(reconsider);
+            matchesDirty = conflictsDirty = true;
+        }
+    }
+
+    protected void reconsider(AddressElement elem) {
+
+        List<Match> reconsider;
+
+        reconsider = getConflicts(elem);
+        if (reconsider != null) {
+            matches.addAll(reconsider);
+            conflicts.removeAll(reconsider);
+            matchesDirty = conflictsDirty = true;
+        }
+    }
+
+    /**
+     * Recreates all {@code *Index} fields if this reasoner.
+     */
+    protected void regenerateIndexes() {
+        boolean assertions = false;
+        assert  assertions = true;
+
+        elemMatchHashIndex.clear();
+        primMatchHashIndex.clear();
+        elemConflictHashIndex.clear();
+        primConflictHashIndex.clear();
+        elemConflictListIndex.clear();
+        primConflictListIndex.clear();
+
+        for (Match match : matches) {
+            elemMatchHashIndex.put(match.elem, match);
+            primMatchHashIndex.put(match.prim, match);
+        }
+
+        for (Match conflict : conflicts) {
+
+            List<Match> elemConflicts = elemConflictHashIndex.get(conflict.elem);
+            if (elemConflicts == null) {
+                elemConflicts = new ArrayList<Match>();
+                elemConflictHashIndex.put(conflict.elem, elemConflicts);
+            }
+            elemConflicts.add(conflict);
+
+            List<Match> primConflicts = primConflictHashIndex.get(conflict.prim);
+            if (primConflicts == null) {
+                primConflicts = new ArrayList<Match>();
+                primConflictHashIndex.put(conflict.prim, primConflicts);
+            }
+            primConflicts.add(conflict);
+        }
+
+        for (AddressElement elem : elemConflictHashIndex.keySet())
+            elemConflictListIndex.add(elem);
+
+        for (OsmPrimitive prim : primConflictHashIndex.keySet())
+            primConflictListIndex.add(prim);
+
+        assert elemConflictHashIndex.size() == elemConflictListIndex.size();
+        assert primConflictHashIndex.size() == primConflictListIndex.size();
+
+        for (AddressElement elem : elemConflictListIndex)
+            assert elemConflictHashIndex.get(elem) != null;
+        for (OsmPrimitive prim : primConflictListIndex)
+            assert primConflictHashIndex.get(prim) != null;
+
+        if (assertions) {
+            System.out.println("Spárovaných dvojic: " + String.valueOf(matches.size()));
+            System.out.println("Konfliktů (celkem): " + String.valueOf(conflicts.size())
+                    + "; " + String.valueOf(elemConflictListIndex.size())
+                    + "+"  + String.valueOf(primConflictListIndex.size())
+                    + " elementů+primitiv");
+        }
+    }
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/intelligence/SelectionMonitor.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/intelligence/SelectionMonitor.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/intelligence/SelectionMonitor.java	(revision 15166)
@@ -0,0 +1,37 @@
+package org.openstreetmap.josm.plugins.czechaddress.intelligence;
+
+import java.util.Collection;
+import org.openstreetmap.josm.data.SelectionChangedListener;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.plugins.czechaddress.CzechAddressPlugin;
+import org.openstreetmap.josm.plugins.czechaddress.NotNullList;
+
+/**
+ * Listenes to the current selection for reasoning
+ * 
+ * <p>Currently JOSM has no way of giving notice about a changed or deleted
+ * node. This class tries to overcome this at the cost of computational
+ * inefficiency. It monitors every de-selected primitive.</p>
+ *
+ * @author Radomír Černoch, radomir.cernoch@gmail.com
+ */
+public class SelectionMonitor implements SelectionChangedListener {
+
+    NotNullList<OsmPrimitive> lastSelection = new NotNullList<OsmPrimitive>();
+
+    public SelectionMonitor() {
+        DataSet.selListeners.add(this);
+    }
+
+    public void selectionChanged(
+                            Collection<? extends OsmPrimitive> newSelection) {
+        Reasoner r = CzechAddressPlugin.getReasoner();
+
+        r.addPrimitives(lastSelection);
+
+        lastSelection.clear();
+        for (OsmPrimitive prim : newSelection)
+            lastSelection.add(prim);
+    }
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/parser/DatabaseParser.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/parser/DatabaseParser.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/parser/DatabaseParser.java	(revision 15166)
@@ -0,0 +1,131 @@
+package org.openstreetmap.josm.plugins.czechaddress.parser;
+
+import org.openstreetmap.josm.plugins.czechaddress.addressdatabase.*;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.plugins.czechaddress.DatabaseLoadException;
+
+/**
+ * General superclass for any parser capable of filling the database.
+ * 
+ * @see Database
+ *
+ * @author Radomír Černoch, raodmir.cernoch@gmail.com
+ */
+public abstract class DatabaseParser {
+
+    /** Directory, where the parser can store cache data. */
+    protected String storageDir = Main.pref.getPreferencesDir();
+
+    /**
+     * Sets the storage directory.
+     */
+    public void setStorageDir(String storageDir) {
+        this.storageDir = storageDir;
+    }
+
+    /** Target Database, which should be filled by the parser. */
+    protected Database target = null;
+
+    /**
+     * Sets the target database, which should be filled by the parser.
+     */
+    public void setTargetDatabase(Database targetDatabase) {
+        target = targetDatabase;
+    }
+
+    /**
+     * The ultimate method, which starts filling the database.
+     * 
+     * @throws DatabaseLoadException if anything goes wrong...
+     */
+    public void fillDatabase() throws DatabaseLoadException {
+
+        assert target != null;
+
+        if (!(new File(getDatabasePath())).exists()) {
+            System.err.println("CzechAddress: Soubor s databází českých adres na disku nenalazen. Pokusím se jej stáhnout.");
+            downloadDatabase();
+        }
+
+        parseDatabase();
+    }
+
+    /**
+     * The internal method, which does the actual parsing.
+     * 
+     * @throws DatabaseLoadException should be thrown if anything gets wrong...
+     */
+    protected abstract void parseDatabase() throws DatabaseLoadException;
+
+    /**
+     * Returns a URL, where the database can be dowloaded from. This method
+     * is used in the {@code dowloadDatabase()} method.
+     */
+    protected abstract String getDatabaseUrl();
+
+    /**
+     * Returns a path, where the database can be stored on local disk.
+     */
+    protected abstract String getDatabasePath();
+
+    /**
+     * Opens the database and provides an input stream to its content.
+     *
+     * @throws DatabaseLoadException should be thrown when the input cannot be opened.
+     */
+    protected abstract InputStream getDatabaseStream() throws DatabaseLoadException;
+
+    /**
+     * Downloads the database from remote URL.
+     *
+     * The URL is provided by {@code getDatabaseUrl()} method.
+     * 
+     * @throws DatabaseLoadException if any error occurs during the download.
+     */
+    protected void downloadDatabase() throws DatabaseLoadException {
+
+        try {
+
+            URL url = new URL(getDatabaseUrl());
+            HttpURLConnection con = (HttpURLConnection)url.openConnection();
+            con.connect();
+
+            System.err.println("CzechAddress: Server vrátil: " + con.getResponseMessage());
+
+            // Check the status error code from server
+            if (con.getResponseCode() != 200)
+                throw new DatabaseLoadException(
+                        "Požadavek na server MVČR selhal, číslo chyby: " + String.valueOf( con.getResponseCode() ));
+
+            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(getDatabasePath()));
+
+            int total = 0, count;
+            byte[] buffer = new byte[1024*128];
+            while ((count = con.getInputStream().read(buffer)) >= 0) {
+                bos.write(buffer, 0, count);
+                total += count;
+                System.err.println("CzechAddress: MVČR database: " + String.valueOf(total/1024) + " kb downloaded.");
+            }
+
+            bos.close();
+
+            // Look for a detailed error message from the server
+            if (con.getHeaderField("Error") != null) {
+                String er = con.getHeaderField("Error");
+                throw new DatabaseLoadException("Server vrátil chybu: " + er);
+            }
+            con.disconnect();
+
+        } catch (IOException ioexp) {
+            ioexp.printStackTrace();
+            throw new DatabaseLoadException("Chyba při načítání databáze");
+        }
+    }
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/parser/MvcrParser.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/parser/MvcrParser.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/parser/MvcrParser.java	(revision 15166)
@@ -0,0 +1,220 @@
+package org.openstreetmap.josm.plugins.czechaddress.parser;
+
+import org.openstreetmap.josm.plugins.czechaddress.addressdatabase.*;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.plugins.czechaddress.DatabaseLoadException;
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+
+/**
+ * Parser for handling database from 'www.mvcr.cz/adresy'.
+ *
+ * @author Radomír Černoch, radomir.cernoch@gmail.com
+ */
+public class MvcrParser extends XMLParser {
+
+    Region curRegion = null;
+    ViToCi curViToCi = null;
+    Suburb curSuburb = null;
+    Street curStreet = null;
+
+//==============================================================================
+//  IMPLEMENTING THE ContentHandler
+//==============================================================================
+
+    public void startElement(String uri, String localName, String name,
+                                Attributes attributes) throws SAXException {
+
+        // ========== PARSING REGION ========== //
+        if (name.equals("oblast")) {
+
+            // If the region filter is on, apply it!
+            if (filRegion != null && !attributes.getValue("nazev").equals(filRegion))
+                return;
+
+            /*curRegion = target.findRegion(
+                attributes.getValue("nazev"),
+                attributes.getValue("kraj"),
+                attributes.getValue("okres"));
+
+            if (curRegion == null) {*/
+                curRegion = new Region(
+                    attributes.getValue("nazev"),
+                    attributes.getValue("kraj"),
+                    attributes.getValue("okres"));
+
+                target.regions.add(curRegion);
+            //}
+        }
+
+        // Everything must belong to some region
+        if (curRegion == null)
+            return;
+
+        // ========== PARSING ViToCI ========== //
+        if (name.equals("obec")) {
+
+            // If the viToCi filter is on, apply it!
+            if (filViToCi != null && !attributes.getValue("nazev").equals(filViToCi))
+                return;
+
+            //curViToCi = curRegion.findViToCi(attributes.getValue("nazev"));
+            //if (curViToCi == null) {
+                curViToCi = new ViToCi(attributes.getValue("nazev"));
+            //    System.out.println("Parser: " + curViToCi);
+                curRegion.addViToCi(curViToCi);
+            //}
+        }
+
+        // ========== PARSING SUBURB ========== //
+        if (name.equals("cast")) {
+            if (curViToCi == null)
+                return;
+
+            // If the suburb filter is on, apply it!
+            if (filSuburb != null && !attributes.getValue("nazev").equals(filSuburb))
+                return;
+
+            //curSuburb = curViToCi.findSuburb(attributes.getValue("nazev"));
+            //if (curSuburb == null) {
+                curSuburb = new Suburb(attributes.getValue("nazev"));
+            //    System.out.println("Parser: " + curSuburb);
+                curViToCi.addSuburb(curSuburb);
+            //}
+        }
+
+        // ========== PARSING STREET ========== //
+        if (name.equals("ulice")) {
+
+            // If the street filter is on, apply it!
+            if (filStreet != null && !attributes.getValue("nazev").equals(filStreet))
+                return;
+
+            ElementWithStreets   topElem = curSuburb;
+            if (topElem == null) topElem = curViToCi;
+            if (topElem == null) topElem = curRegion;
+
+            //curStreet = topElem.findStreet(attributes.getValue("nazev"));
+            //if (curStreet == null) {
+                curStreet = new Street(attributes.getValue("nazev"));
+            //    System.out.println("Parser: " + curStreet);
+                topElem.addStreet(curStreet);
+            //}
+
+        }
+
+        // ========== PARSING HOUSE ========== //
+        if (name.equals("a")) {
+
+            if (   (attributes.getValue("p") == null)
+                && (attributes.getValue("o") == null))
+                return;
+
+            ElementWithHouses    topElem = curStreet;
+            if (topElem == null) topElem = curSuburb;
+            if (topElem == null) topElem = curViToCi;
+            if (topElem == null) topElem = curRegion;
+
+            topElem.addHouse(new House(attributes.getValue("p"),
+                                       attributes.getValue("o")));
+        }
+    }
+
+    @Override
+    public void endElement(String uri, String localName, String name)
+                                                   throws SAXException {
+        if (name.equals("cast")) {
+            curSuburb = null;
+
+        } else if (name.equals("obec")) {
+            curSuburb = null;
+            curViToCi = null;
+
+        } else if (name.equals("oblast")) {
+            curSuburb = null;
+            curViToCi = null;
+            curRegion = null;
+
+        } else if (name.equals("ulice")) {
+            curStreet = null;
+        }
+    }
+
+    public void setDocumentLocator(Locator locator) {}
+    public void startDocument() throws SAXException {}
+    public void endDocument() throws SAXException {}
+    public void startPrefixMapping(String prefix, String uri) throws SAXException {}
+    public void endPrefixMapping(String prefix) throws SAXException {}
+    public void characters(char[] ch, int start, int length) throws SAXException {}
+    public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {}
+    public void processingInstruction(String target, String data) throws SAXException {}
+    public void skippedEntity(String name) throws SAXException {}
+
+//==============================================================================
+//  FILTERING
+//==============================================================================
+
+    String filRegion = null;
+    String filViToCi = null;
+    String filSuburb = null;
+    String filStreet = null;
+
+    public void setFilter(String filterRegion, String filterViToCi,
+                          String filterSuburb, String filterStreet) {
+
+        if (filterRegion != null) filRegion = filterRegion.toUpperCase();
+        if (filterViToCi != null) filViToCi = filterViToCi.toUpperCase();
+        if (filterSuburb != null) filSuburb = filterSuburb.toUpperCase();
+        if (filterStreet != null) filStreet = filterStreet.toUpperCase();
+    }
+
+//==============================================================================
+//  IMPLEMENTING THE DatabaseParser
+//==============================================================================
+
+    static final String URL_DEFAULT = "http://web.mvcr.cz/adresa/adresy.zip";
+    static final String URL_PREFERENCES_KEY = "czechaddress.databaseurl";
+    
+    @Override
+    protected String getDatabaseUrl() {
+
+        if (!Main.pref.hasKey(URL_PREFERENCES_KEY))
+            Main.pref.put(URL_PREFERENCES_KEY, URL_DEFAULT);
+
+        return Main.pref.get(URL_PREFERENCES_KEY);
+    }
+
+    @Override
+    protected String getDatabasePath() {
+        return storageDir + "-adresy.zip";
+    }
+
+    protected InputStream getDatabaseStream() throws DatabaseLoadException {
+        ZipInputStream zis;
+        ZipEntry zipEntry = null;
+        try {
+	    zis = new ZipInputStream(new FileInputStream(getDatabasePath()));
+
+	    while ((zipEntry = zis.getNextEntry()) != null)
+	    	if (zipEntry.getName().equals("adresy.xml"))
+	    		break;
+
+        } catch (IOException ioexp) {
+            throw new DatabaseLoadException("Chyba při čtení archivu s databází.");
+        }
+
+        if (zipEntry == null)
+	    throw new DatabaseLoadException(
+                    "ZIP archiv s databází neobsahuje soubor 'adresy.xml'.");
+
+        return zis;
+    }
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/parser/XMLParser.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/parser/XMLParser.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/parser/XMLParser.java	(revision 15166)
@@ -0,0 +1,39 @@
+package org.openstreetmap.josm.plugins.czechaddress.parser;
+
+import org.openstreetmap.josm.plugins.czechaddress.parser.DatabaseParser;
+import java.io.IOException;
+import org.openstreetmap.josm.plugins.czechaddress.DatabaseLoadException;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+/**
+ * Any XML parser capable of filling the database.
+ *
+ * <p>This class has is capable of initialising the parser and handling errors,
+ * but parsing the actual document must be done by subclasses.</p>
+ *
+ * @see Database
+ *
+ * @author Radomír Černoch, raodmir.cernoch@gmail.com
+ */
+public abstract class XMLParser extends DatabaseParser
+                                implements ContentHandler {
+
+    protected void parseDatabase() throws DatabaseLoadException {
+
+        try {
+            XMLReader xr;
+            xr = XMLReaderFactory.createXMLReader();
+            xr.setContentHandler(this);
+            xr.parse(new InputSource(getDatabaseStream()));
+
+        } catch (IOException ioexp) {
+            throw new DatabaseLoadException("Chyba při čtení archivu s databází.");
+        } catch (SAXException saxexp) {
+            throw new DatabaseLoadException("Selhaho parsování XML souboru s databází adres.");
+        }
+    }
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/proposal/AddKeyValueProposal.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/proposal/AddKeyValueProposal.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/proposal/AddKeyValueProposal.java	(revision 15166)
@@ -0,0 +1,45 @@
+package org.openstreetmap.josm.plugins.czechaddress.proposal;
+
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+
+/**
+ * Proposal for adding a key-value attribute.
+ *
+ * @author Radomír Černoch radomir.cernoch@gmail.com
+ */
+public class AddKeyValueProposal extends Proposal {
+
+    String key;
+    String val;
+
+    /**
+     * Default constructor setting the internal key-value pair.
+     * @param key key of the new attribute
+     * @param val value of the new attribute
+     */
+    public AddKeyValueProposal(String key, String val) {
+        this.key = key;
+        this.val = val;
+    }
+
+    /**
+     * Adds the internally stored key-value pair to
+     * a given {@link OsmPrimitive}.
+     */
+    @Override
+    public void apply(OsmPrimitive op) {
+        super.apply(op);
+        op.put(key, val);
+    }
+
+    /**
+     * Provides textual representation of this modification.
+     *
+     * Currently the string is in Czech language (see {@link CzechAddressPlugin}).
+     */
+    @Override
+    public String toString() {
+        return "Přidat '" + key + "=" + val + "'";
+    }
+
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/proposal/ExtractAddressIntoNodeProposal.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/proposal/ExtractAddressIntoNodeProposal.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/proposal/ExtractAddressIntoNodeProposal.java	(revision 15166)
@@ -0,0 +1,87 @@
+package org.openstreetmap.josm.plugins.czechaddress.proposal;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
+
+/**
+ * Extracts address from a {@link Way} and creates
+ * a standalone {@link Node} with the address information.
+ *
+ * @author Radomír Černoch radomir.cernoch@gmail.com
+ */
+public class ExtractAddressIntoNodeProposal extends Proposal {
+
+    /**
+     * Tells whether this proposal is applicable to the given primitive.
+     *
+     * The primitive must be a {@link Way} and it must contain at least
+     * one tag starting with 'addr:'. Then {@code true} is returned.
+     * Otherwise false.
+     *
+     * @param primitive tested primitive
+     * @return true if this proposal is applicable
+     */
+    static public boolean isApplicable(OsmPrimitive primitive) {
+        if (!(primitive instanceof Way))
+            return false;
+
+        for (String key : primitive.keySet())
+            if (key.startsWith("addr:"))
+                return true;
+
+        return false;
+    }
+
+    /**
+     * Extracts the address information from the given primitive into
+     * a newly created {@link Node}. The new node is added into the
+     * JOSM database.
+     *
+     * If the extraction in not applicable, nothing happens.
+     *
+     * @param primitive the {@link Way} from which the address will be extracted
+     */
+    @Override
+    public void apply(OsmPrimitive primitive) {
+
+        if (!isApplicable(primitive))
+            return;
+        
+        Way way = (Way) primitive;
+
+        BoundingXYVisitor visitor = new BoundingXYVisitor();
+        way.visit(visitor);
+        LatLon center = new LatLon(
+                (visitor.getBounds().max.lat() + visitor.getBounds().min.lat())/2,
+                (visitor.getBounds().max.lon() + visitor.getBounds().min.lon())/2
+            );
+        
+        Node addrNode = new Node(center);
+
+        for (String key : way.keySet())
+            if (key.startsWith("addr"))
+                addrNode.put(key, way.get(key));
+
+        for (String key : addrNode.keySet())
+           way.remove(key);
+
+        Main.ds.addPrimitive(addrNode);
+    }
+
+    /**
+     * Returns textual representation of this proposal.
+     * 
+     * Currently the string is in Czech language (see {@link CzechAddressPlugin}).
+     */
+    @Override
+    public String toString() {
+        return "Vytvořit z budovy samostatný adresní uzel.";
+    }
+
+    
+
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/proposal/KeyValueChangeProposal.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/proposal/KeyValueChangeProposal.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/proposal/KeyValueChangeProposal.java	(revision 15166)
@@ -0,0 +1,39 @@
+package org.openstreetmap.josm.plugins.czechaddress.proposal;
+
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+
+/**
+ * Proposal changing key-value pair to different pair.
+ * @author Radomír Černoch, radomir.cernoch@gmail.com
+ */
+public class KeyValueChangeProposal extends Proposal {
+
+    String oldKey;
+    String oldVal;
+    String newKey;
+    String newVal;
+
+    public KeyValueChangeProposal(String oldKey, String oldVal,
+                                     String newKey, String newVal) {
+        this.oldKey = oldKey;
+        this.oldVal = oldVal;
+        this.newKey = newKey;
+        this.newVal = newVal;
+    }
+
+    @Override
+    public void apply(OsmPrimitive op) {
+        super.apply(op);
+        op.put(newKey, newVal);
+    }
+
+    @Override
+    public String toString() {
+        if (oldKey.equals(newKey))
+            return "Hodnotu '" + oldKey + "=" + oldVal + "' změnit na '" + newVal + "'";
+        else
+            return "Nahradit '" + oldKey + "=" + oldVal + "' za '" + newKey + "=" + newVal + "'";
+    }
+
+
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/proposal/Proposal.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/proposal/Proposal.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/proposal/Proposal.java	(revision 15166)
@@ -0,0 +1,36 @@
+package org.openstreetmap.josm.plugins.czechaddress.proposal;
+
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+
+/**
+ * Represents a single modification of an {@link OsmPrimitive}.
+ *
+ * This class is intended to be subclassed to provide standard modifications
+ * such as "adding a key-value pair" {@link AddKeyValueAlternation}.
+ *
+ * @author Radomír Černoch radomir.cernoch@gmail.com
+ * @see ProposalContainer
+ * @see AddKeyValueProposal
+ * @see RemoveKeyProposal
+ * @see KeyValueChangeProposal
+ * @see ExtractAddressIntoNodeProposal
+ */
+abstract public class Proposal {
+
+    /**
+     * Applies the stored modification to the given {@link OsmPrimitive}.
+     * @param op the primitive to be altered
+     */
+    public void apply(OsmPrimitive op) {
+        op.modified = true;
+    }
+
+    /**
+     * Provides textual representation of this modification.
+     *
+     * @return the string representation
+     */
+    @Override
+    abstract public String toString();
+
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/proposal/ProposalContainer.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/proposal/ProposalContainer.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/proposal/ProposalContainer.java	(revision 15166)
@@ -0,0 +1,218 @@
+package org.openstreetmap.josm.plugins.czechaddress.proposal;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import javax.swing.JTree;
+import javax.swing.ListModel;
+import javax.swing.event.ListDataEvent;
+import javax.swing.event.ListDataListener;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+
+/**
+ * Class encapsulating an {@link OsmPrimitive} and a list of proposed
+ * {@link Proposal}.
+ *
+ * List of these objects are usually stored in a {@link ProposalDatabase},
+ * which also provides means of displaying is in a {@link JTree}.
+ *
+ * @author Radomír Černoch radomir.cernoch@gmail.com
+ * @see ProposalDatabase
+ * @see OsmPrimitive
+ * @see Proposal
+ */
+public class ProposalContainer implements ListModel {
+
+
+    /**
+     * List of listeners for implementing the {@code ListModel}.
+     */
+    private List<ListDataListener> listeners =
+            new ArrayList<ListDataListener>();
+
+    /**
+     * The internal reference to an {@link OsmPrimitive}, to which the
+     * proposals shall be applied.
+     */
+    protected OsmPrimitive target;
+
+    /**
+     * Default constructor setting the internal reference to target
+     * {@code OsmPrimitive}.
+     */
+    public ProposalContainer(OsmPrimitive target) {
+        this.target = target;
+    }
+
+    /**
+     * Sets a new target, to which the {@link Proposal}s should be applied.
+     */
+    public void setTarget(OsmPrimitive newTarget) {
+        this.target = newTarget;
+    }
+
+    /**
+     * Returns the target, to which the {@link Proposal}s should be applied.
+     */
+    public OsmPrimitive getTarget() {
+        return target;
+    }
+
+//==============================================================================
+
+    /**
+     * The list of proposals to be applied to encapsulated primitive.
+     */
+    protected List<Proposal> proposals
+            = new ArrayList<Proposal>();
+
+    /**
+     * Adds a new {@link Proposal}.
+     * @param a new alternation to be added
+     */
+    public void addProposal(Proposal a) {
+        proposals.add(a);
+
+        ListDataEvent evt = new ListDataEvent(
+                this,
+                ListDataEvent.INTERVAL_ADDED,
+                proposals.size()-1,
+                proposals.size()-1);
+
+        for (ListDataListener l : listeners)
+            l.contentsChanged(evt);
+    }
+
+    /**
+     * Adds a new set of {@link Proposal}s.
+     * @param a collection of proposals to be added
+     */
+    public void addProposals(Collection<Proposal> a) {
+
+        int index1 = proposals.size();
+        proposals.addAll(a);
+        int index2 = proposals.size()-1;
+
+        ListDataEvent evt = new ListDataEvent(
+                this,
+                ListDataEvent.INTERVAL_ADDED,
+                index1, index2);
+
+        for (ListDataListener l : listeners)
+            l.contentsChanged(evt);
+    }
+
+
+    /**
+     * Removes the give proposal from the list of proposals.
+     * @param proposal the proposal to be removed
+     */
+    public void removeProposal(Proposal proposal) {
+
+        int index = proposals.indexOf(proposal);
+        if (index == -1)
+            return;
+
+        proposals.remove(index);
+
+        ListDataEvent evt = new ListDataEvent(
+                this,
+                ListDataEvent.INTERVAL_REMOVED,
+                index, index);
+
+        for (ListDataListener l : listeners)
+            l.contentsChanged(evt);
+    }
+
+    /**
+     * Replaces the internal list of proposals with the given one.
+     * @param a collection of proposals to be added
+     */
+    public void setProposals(List<Proposal> proposals) {
+        this.proposals = proposals;
+
+        ListDataEvent evt = new ListDataEvent(
+                this, ListDataEvent.CONTENTS_CHANGED,
+                0, proposals.size()-1);
+
+        for (ListDataListener l : listeners)
+            l.contentsChanged(evt);
+    }
+
+    /**
+     * @return the list of proposed proposals.
+     */
+    public List<Proposal> getProposals() {
+        return proposals;
+    }
+
+    /**
+     * Removes all proposals.
+     */
+    public void clear() {
+        ListDataEvent evt = new ListDataEvent(
+                this,
+                ListDataEvent.INTERVAL_REMOVED,
+                0, proposals.size()-1);
+
+        proposals.clear();
+
+        for (ListDataListener l : listeners)
+            l.contentsChanged(evt);
+    }
+
+    /**
+     * Applies all stored {@link Proposal}s to the target {@link OsmPrimitive}.
+     */
+    public void applyAll() {
+        for (Proposal proposal : proposals)
+            proposal.apply(target);
+    }
+
+//==============================================================================
+
+    /**
+     * Tries to decode the name of the referenced primitive. Otherwise
+     * it returns the ID of that primitive.
+     *
+     * Currently the string is in Czech language (see {@link CzechAddressPlugin}).
+     */
+    @Override
+    public String toString() {
+        /*if (target.keySet().contains("name"))
+            return target.get("name");
+
+        if (   target.keySet().contains("addr:alternatenumber")
+            || target.keySet().contains("addr:housenumber")) {
+
+            String cp = target.get("addr:alternatenumber");
+            String co = target.get("addr:housenumber");
+            String ul = target.get("addr:street");
+
+            if (cp == null) cp = "?";
+            if (co == null) co = "?";
+            if (ul == null) ul = "" ; else ul = " " + ul;
+
+            return "Dům " + String.valueOf(cp) + "/" + String.valueOf(co) + ul;
+        }*/
+
+        return target.toString();
+    }
+
+    public int getSize() {
+        return proposals.size();
+    }
+
+    public Object getElementAt(int index) {
+        return proposals.get(index);
+    }
+
+    public void addListDataListener(ListDataListener l) {
+        listeners.add(l);
+    }
+
+    public void removeListDataListener(ListDataListener l) {
+        listeners.remove(l);
+    }
+
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/proposal/ProposalDatabase.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/proposal/ProposalDatabase.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/proposal/ProposalDatabase.java	(revision 15166)
@@ -0,0 +1,228 @@
+package org.openstreetmap.josm.plugins.czechaddress.proposal;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import javax.swing.event.TreeModelEvent;
+import javax.swing.event.TreeModelListener;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreePath;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+
+/**
+ * Stores a list of {@link ProposalContainer}s, which represent the whole
+ * changeset of alternations to be applied to some nodes.
+ *
+ * ProposalDatabase also impemets {@link TreeModel}, which allows an easy
+ * display of such object in an {@link TreeView}.
+ *
+ * @author Radomír Černoch radomir.cernoch@gmail.com
+ */
+public class ProposalDatabase implements TreeModel {
+
+    /**
+     * The internal database of {@link ProposalContainer}s.
+     */
+    protected List<ProposalContainer> changeSet =
+             new ArrayList<ProposalContainer>();
+
+    /**
+     * Listeners for the {@link TreeModel} interface.
+     */
+    protected List<TreeModelListener> listeners =
+            new ArrayList<TreeModelListener>();
+
+    /**
+     * The root element for the {@link TreeView}.
+     */
+    protected String root = new String("Navrhované změny");
+
+    /**
+     * Adds a new {@link ProposalContainer} to the internal database.
+     * @param pac
+     */
+    public void addContainer(ProposalContainer newContainer) {
+        assert !changeSet.contains(newContainer)
+             : "Containers in the database must unique.";
+
+        changeSet.add(newContainer);
+    }
+
+    /**
+     * Removes the given {@link ProposalContainer} from the internal list.
+     * @param containerToAdd
+     */
+    public void removeContainer(ProposalContainer containerToAdd) {
+        changeSet.remove(containerToAdd);
+    }
+
+    /**
+     * Finds a {@link PropsalContainer} containing given {@code Primitive}.
+     *
+     * <p>If no container with the given primitive is found, null is returned.
+     * If there are multiple containers (which should not be the case),
+     * the first is returned.</p>
+     *
+     * @param primitive the primitive to be found
+     * @return the ProposalContainer containing primitive or null
+     */
+    public ProposalContainer findContainer(OsmPrimitive primitive) {
+        for (ProposalContainer pac : changeSet)
+            if (pac.getTarget().equals(primitive))
+                    return pac;
+        return null;
+    }
+
+    /**
+     * Adds proposals corresponding to a primitive into the database.
+     *
+     * <p>If the primitive is already in the database, the proposal
+     * is added to its container. If not, a new container is created.</p>
+     *
+     * @param primitive
+     * @param proposal
+     */
+    public void addProposals(OsmPrimitive primitive,
+                             Collection<Proposal> proposal) {
+        
+        ProposalContainer container = findContainer(primitive);
+        if (container == null) {
+            container = new ProposalContainer(primitive);
+            addContainer(container);
+        }
+
+        container.addProposals(proposal);
+    }
+
+    /**
+     * Replaces the internal changeset of {@link ProposalContainer}s
+     * with a new one.
+     *
+     * @param newChangeSet new changeset to replace the current one
+     */
+    public void setContainers(ArrayList<ProposalContainer> newChangeSet) {
+        this.changeSet = newChangeSet;
+    }
+
+    /**
+     * Removes all {@link ProposalContainer}s from the changeset.
+     */
+    public void clear() {
+        changeSet.clear();
+    }
+
+    /**
+     * Gives a reference to the internal list of {@link ProposalContainer}s.
+     * 
+     * @return the refernence to internal changeset.
+     */
+    public List<ProposalContainer> getContainers() {
+        return changeSet;
+    }
+
+    /**
+     * Applies all {@link Proposal}s in all {@link ProposalContainer}s.
+     */
+    public void applyAll() {
+        for (ProposalContainer a : changeSet)
+            a.applyAll();
+    }
+
+//==============================================================================
+//  IMPLEMENTATION OF THE TREEMODEL INTERFACE
+//==============================================================================
+
+    public Object getRoot() {
+        return root;
+    }
+
+    public Object getChild(Object parent, int index) {
+        if (parent.equals(root))
+            return changeSet.get(index);
+
+        if (parent instanceof ProposalContainer)
+            return ((ProposalContainer) parent).getProposals().get(index);
+
+        return null;
+    }
+
+    public int getChildCount(Object parent) {
+        if (parent.equals(root))
+            return changeSet.size();
+
+        if (parent instanceof ProposalContainer)
+            return ((ProposalContainer) parent).getProposals().size();
+
+        return 0;
+    }
+
+    public boolean isLeaf(Object node) {
+        if (node.equals(root))
+            return changeSet.size() == 0;
+
+        if (node instanceof ProposalContainer)
+            return ((ProposalContainer) node).getProposals().size() == 0;
+
+        return true;
+    }
+
+    public void valueForPathChanged(TreePath path, Object newValue) {
+        // We are a read-only model... Nothing to do here.
+    }
+
+    public int getIndexOfChild(Object parent, Object child) {
+        if (parent.equals(root))
+            return changeSet.indexOf(child);
+
+        if (parent instanceof ProposalContainer)
+            return ((ProposalContainer) parent).getProposals().indexOf(child);
+
+        return -1;
+    }
+
+    public void addTreeModelListener(TreeModelListener l) {
+        listeners.add(l);
+    }
+
+    public void removeTreeModelListener(TreeModelListener l) {
+        listeners.remove(l);
+    }
+
+    /**
+     * Deletes a proposal at the given tree path.
+     *
+     * @param path path containing the element to be deleted
+     */
+    public void deteleObjectAtPath(TreePath path) {
+
+        // The root element cannot be deleted
+        if (path.getPathCount() <= 1)
+            return;
+
+
+        // If path-length is 2, the whole ProposalContainer is deleted.
+        if (path.getPathCount() == 2) {
+            changeSet.remove((ProposalContainer) path.getPathComponent(1));
+
+            TreeModelEvent event = new TreeModelEvent(this, path);
+            for (TreeModelListener l : listeners)
+                l.treeNodesRemoved(event);
+            return;
+        }
+
+
+        // If path-length is 3, only a single Proposal is deleted.
+        ProposalContainer ac = (ProposalContainer) path.getPathComponent(1);
+        if (path.getPathCount() == 3) {
+            ac.getProposals().remove((Proposal) path.getPathComponent(2));
+
+            TreeModelEvent event = new TreeModelEvent(this, path);
+            for (TreeModelListener l : listeners)
+                l.treeNodesRemoved(event);
+            return;
+        }
+
+        assert false : path;
+        return;
+    }
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/proposal/ProposalFactory.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/proposal/ProposalFactory.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/proposal/ProposalFactory.java	(revision 15166)
@@ -0,0 +1,84 @@
+package org.openstreetmap.josm.plugins.czechaddress.proposal;
+
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.plugins.czechaddress.addressdatabase.AddressElement;
+
+/**
+ * Helpers allowing to create {@link Proposals}s easily.
+ *
+ * <p>Methods from this class are typically called from
+ * {@link AddressElement}{@code .getDiff()} when the user wants to copy
+ * attributes from an {@code AddressElement} to {@link OsmPrimitive}.
+ *
+ * @author Radomír Černoch, radomir.cernoch@gmail.com
+ */
+public abstract class ProposalFactory {
+
+    /**
+     * Unifies two strings to create a perfect match.
+     *
+     * @param key name of the attribute (eg. highway); not used when matching,
+     * just passed to newly created {@link Proposal} to inform the user
+     * @param current original value of the attribute (eg. residental);
+     * usually the current value of some {@link OsmPrimitive}'s attribute
+     * @param target the target value of the attribute (eg. service);
+     * usually the value of some {@link AddressElement}.
+     * @return the {@link Proposal}, whose application unifies <i>current</i>
+     * and <i>target</i>
+     */
+    public static Proposal getStringFieldDiff(
+            String key, String current, String target) {
+
+        if (target == null) {
+            if (current != null)
+                return new RemoveKeyProposal(key);
+            else
+                return null;
+        }
+
+        if (current == null)
+            return new AddKeyValueProposal(key, target);
+
+        if (!current.toUpperCase().equals(target.toUpperCase()))
+            return new KeyValueChangeProposal(
+                        key, current,
+                        key, target);
+
+        return null;
+    }
+
+    /**
+     * Makes the {@code current} string contain the {@code target}.
+     *
+     * <p>Some attributes consist of several values, which are delimited
+     * by <tt>,</tt> or <tt>;</tt>. This checks if current list contains
+     * such a value and if not, it's added.</p>
+     *
+     * @param key name of the attribute (eg. 'source'); not used when matching,
+     * just passed to newly created {@link Proposal} to inform the user
+     * @param current original value of the attribute (eg. 'cuzk');
+     * usually the current value of some {@link OsmPrimitive}'s attribute
+     * @param target a value that {@code current} should contain (eg. 'mvcr');
+     * @return the {@link Proposal}, whose application unifies includes
+     * {@code current} in {@code target} (eg. modify 'source' to
+     * 'source=cuzk;mvcr')
+     */
+    public static Proposal getListFieldDiff(
+            String key, String current, String target) {
+
+        if (target == null)
+            return null;
+
+        if (current == null)
+            return new AddKeyValueProposal(key, target);
+
+        for (String itemRaw : current.split(","))
+            for (String itemSplitted : itemRaw.split(";"))
+                if (itemSplitted.trim().equals(target.trim()))
+                    return null;
+
+        return new KeyValueChangeProposal(
+                     key, current,
+                     key, current + ";" + target);
+    }
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/proposal/ProposalListPainter.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/proposal/ProposalListPainter.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/proposal/ProposalListPainter.java	(revision 15166)
@@ -0,0 +1,38 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package org.openstreetmap.josm.plugins.czechaddress.proposal;
+
+import java.awt.Component;
+import javax.swing.DefaultListCellRenderer;
+import javax.swing.Icon;
+import javax.swing.JList;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+/**
+ *
+ * @author radek
+ */
+public class ProposalListPainter extends DefaultListCellRenderer {
+
+    Icon iconAdd = ImageProvider.get("actions", "add.png");
+    Icon iconEdit = ImageProvider.get("actions", "edit.png");
+    Icon iconRemove = ImageProvider.get("actions", "remove.png");
+
+    @Override
+    public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
+        Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
+
+        setIcon(null);
+        
+             if (value instanceof AddKeyValueProposal)    setIcon(iconAdd);
+        else if (value instanceof KeyValueChangeProposal) setIcon(iconEdit);
+        else if (value instanceof RemoveKeyProposal)      setIcon(iconRemove);
+
+        return c;
+    }
+    
+
+}
Index: /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/proposal/RemoveKeyProposal.java
===================================================================
--- /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/proposal/RemoveKeyProposal.java	(revision 15166)
+++ /applications/editors/josm/plugins/czechaddress/src/org/openstreetmap/josm/plugins/czechaddress/proposal/RemoveKeyProposal.java	(revision 15166)
@@ -0,0 +1,43 @@
+package org.openstreetmap.josm.plugins.czechaddress.proposal;
+
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+
+/**
+ * Proposal for removing a key-value pair.
+ *
+ * @author Radomír Černoch radomir.cernoch@gmail.com
+ */
+public class RemoveKeyProposal extends Proposal {
+
+    String key;
+
+    /**
+     * Default constructor, which stores the 'key' name to be removed.
+     * @param key the name of the key to be removed
+     */
+    public RemoveKeyProposal(String key) {
+        this.key = key;
+    }
+
+    /**
+     * Removes the key from the specified primitive.
+     * @param op primitive, from which the key will be removed
+     */
+    @Override
+    public void apply(OsmPrimitive op) {
+        super.apply(op);
+        op.remove(key);
+    }
+
+    /**
+     * Textual representation of this proposal.
+     *
+     * Currently the string is in Czech language (see {@link CzechAddressPlugin}).
+     */
+    @Override
+    public String toString() {
+        return "Odstranit atribut '" + key + "'";
+    }
+
+
+}
