Index: src/utilsplugin2/UtilsPlugin2.java
===================================================================
--- src/utilsplugin2/UtilsPlugin2.java	(revision 27495)
+++ src/utilsplugin2/UtilsPlugin2.java	(working copy)
@@ -1,18 +1,30 @@
 // License: GPL v2 or later. See LICENSE file for details.
 package utilsplugin2;
 
+import static org.openstreetmap.josm.tools.I18n.tr;
+
 import utilsplugin2.customurl.ChooseURLAction;
 import utilsplugin2.customurl.OpenPageAction;
 import utilsplugin2.customurl.UtilsPluginPreferences;
 
 import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
 import java.awt.event.KeyEvent;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
 import javax.swing.JMenu;
 import javax.swing.JMenuItem;
 import utilsplugin2.selection.*;
 import utilsplugin2.dumbutils.*;
 
 import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.search.PushbackTokenizer;
+import org.openstreetmap.josm.actions.search.SearchCompiler;
+import org.openstreetmap.josm.actions.search.SearchCompiler.*;
+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.gui.MainMenu;
 import org.openstreetmap.josm.gui.MapFrame;
 import org.openstreetmap.josm.plugins.Plugin;
@@ -89,7 +101,8 @@
         
         selectURL = MainMenu.add(toolsMenu, new ChooseURLAction());
         
-
+        // register search operators
+        SearchCompiler.addMatchFactory(new UtilsUnaryMatchFactory());
     }
 
     @Override
@@ -126,5 +139,100 @@
     public PreferenceSetting getPreferenceSetting() {
         return new UtilsPluginPreferences();
     }
+    
+    public static class UtilsUnaryMatchFactory implements UnaryMatchFactory {
+        private static Collection<String> keywords = Arrays.asList("inside",
+                "intersecting", "allintersecting");
+        
+        @Override
+        public UnaryMatch get(String keyword, Match matchOperand, PushbackTokenizer tokenizer) throws ParseError {
+            if ("inside".equals(keyword))
+                return new InsideMatch(matchOperand);
+            else if ("intersecting".equals(keyword))
+                return new IntersectingMatch(matchOperand, false);
+            else if ("allintersecting".equals(keyword))
+                return new IntersectingMatch(matchOperand, true);
+            return null;
+        }
 
+        @Override
+        public Collection<String> getKeywords() {
+            return keywords;
+        }
+    }
+
+    /**
+     * Matches all objects contained within the match expression.
+     */
+    public static class InsideMatch extends UnaryMatch {
+        private Collection<OsmPrimitive> inside = null;
+        
+        public InsideMatch(Match match) {
+            super(match);
+            init();
+        }
+        
+        /**
+         * Find all objects inside areas which match the expression
+         */
+        private void init() {
+            Collection<OsmPrimitive> matchedAreas = new HashSet<OsmPrimitive>();
+
+            // find all ways that match the expression
+            Collection<Way> ways = Main.main.getCurrentDataSet().getWays();
+            for (Way way : ways) {
+                if (match.match(way))
+                    matchedAreas.add(way);
+            }
+            
+            // find all relations that match the expression
+            Collection<Relation> rels = Main.main.getCurrentDataSet().getRelations();
+            for (Relation rel : rels) {
+                if (match.match(rel))
+                    matchedAreas.add(rel);
+            }
+            
+            inside = NodeWayUtils.selectAllInside(matchedAreas, Main.main.getCurrentDataSet());
+        }
+
+        @Override
+        public boolean match(OsmPrimitive osm) {
+            return inside.contains(osm);
+        }
+    }
+    
+    public static class IntersectingMatch extends UnaryMatch {
+        private Collection<Way> intersecting = null;
+        
+        public IntersectingMatch(Match match, boolean all) {
+            super(match);
+            init(all);
+        }   
+        
+        /**
+         * Find (all) ways intersecting ways which match the expression.
+         */
+        private void init(boolean all) {
+            Collection<Way> matchedWays = new HashSet<Way>();
+            
+            // find all ways that match the expression
+            Collection<Way> allWays = Main.main.getCurrentDataSet().getWays();
+            for (Way way : allWays) {
+                if (match.match(way))
+                    matchedWays.add(way);
+            }
+            
+            Set<Way> newWays = new HashSet<Way>();
+            if (all)
+                NodeWayUtils.addWaysIntersectingWaysRecursively(allWays, matchedWays, newWays);
+            else
+                NodeWayUtils.addWaysIntersectingWays(allWays, matchedWays, newWays);
+            intersecting = newWays;
+        }
+        
+        @Override
+        public boolean match(OsmPrimitive osm) {
+            return intersecting.contains(osm);
+        }
+    }
 }
Index: src/utilsplugin2/selection/SelectAllInsideAction.java
===================================================================
--- src/utilsplugin2/selection/SelectAllInsideAction.java	(revision 27495)
+++ src/utilsplugin2/selection/SelectAllInsideAction.java	(working copy)
@@ -14,10 +14,7 @@
 
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.JosmAction;
-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.data.osm.*;
 import org.openstreetmap.josm.tools.Shortcut;
 
 /**
@@ -31,32 +28,14 @@
                 KeyEvent.VK_I, Shortcut.GROUP_EDIT ,KeyEvent.ALT_DOWN_MASK|KeyEvent.SHIFT_DOWN_MASK), true);
         putValue("help", ht("/Action/SelectAllInside"));
     }
-
+    
+    @Override
     public void actionPerformed(ActionEvent e) {
         long t=System.currentTimeMillis();
-        Set<Way> selectedWays = OsmPrimitive.getFilteredSet(getCurrentDataSet().getSelected(), Way.class);
-        Set<Relation> selectedRels = OsmPrimitive.getFilteredSet(getCurrentDataSet().getSelected(), Relation.class);
-
-        for (Relation r: selectedRels) {
-            if (!r.isMultipolygon()) selectedRels.remove(r);
-        }
-
-        Set<Way> newWays = new HashSet<Way>();
-        Set<Node> newNodes = new HashSet<Node>();
-        // select ways attached to already selected ways
-        if (!selectedWays.isEmpty()) {
-            for (Way w: selectedWays) {
-                NodeWayUtils.addAllInsideWay(getCurrentDataSet(),w,newWays,newNodes);
-            }
-        }
-        if (!selectedRels.isEmpty()) {
-            for (Relation r: selectedRels) {
-                NodeWayUtils.addAllInsideMultipolygon(getCurrentDataSet(),r,newWays,newNodes);
-            }
-        }
-        if (!newWays.isEmpty() || !newNodes.isEmpty()) {
-            getCurrentDataSet().addSelected(newWays);
-            getCurrentDataSet().addSelected(newNodes);
+        Collection<OsmPrimitive> insideSelected = NodeWayUtils.selectAllInside(getCurrentDataSet().getSelected(), getCurrentDataSet());
+        
+        if (!insideSelected.isEmpty()) {
+            getCurrentDataSet().addSelected(insideSelected);
         } else{
         JOptionPane.showMessageDialog(Main.parent,
                tr("Nothing found. Please select some closed ways or multipolygons to find all primitives inside them!"),
Index: src/utilsplugin2/selection/NodeWayUtils.java
===================================================================
--- src/utilsplugin2/selection/NodeWayUtils.java	(revision 27495)
+++ src/utilsplugin2/selection/NodeWayUtils.java	(working copy)
@@ -147,7 +147,7 @@
      * @param initWays ways to check intersections
      * @param newWays set to place the ways we found
      */
-    static int addWaysIntersectingWays(Collection<Way> allWays, Collection<Way> initWays, Set<Way> newWays) {
+    public static int addWaysIntersectingWays(Collection<Way> allWays, Collection<Way> initWays, Set<Way> newWays) {
         int count=0;
         for (Way w : initWays){
             count+=addWaysIntersectingWay(allWays, w, newWays);
@@ -171,7 +171,7 @@
         return newNodes.size()-s;
     }
 
-    static void addWaysIntersectingWaysRecursively
+    public static void addWaysIntersectingWaysRecursively
             (Collection<Way> allWays, Collection<Way> initWays, Set<Way> newWays)
     {
             Set<Way> foundWays = new HashSet<Way>();
@@ -457,5 +457,36 @@
        // System.out.printf("Intersected intercount %d %s\n",interCount, point.toString());
         if (interCount%2 == 1) return 1; else return 0;
     }
+    
+    public static Collection<OsmPrimitive> selectAllInside(Collection<OsmPrimitive> selected, DataSet dataset) {
+        Set<Way> selectedWays = OsmPrimitive.getFilteredSet(selected, Way.class);
+        Set<Relation> selectedRels = OsmPrimitive.getFilteredSet(selected, Relation.class);
 
+        for (Relation r: selectedRels) {
+            if (!r.isMultipolygon()) selectedRels.remove(r);
+        }
+
+        Set<Way> newWays = new HashSet<Way>();
+        Set<Node> newNodes = new HashSet<Node>();
+        // select ways attached to already selected ways
+        if (!selectedWays.isEmpty()) {
+            for (Way w: selectedWays) {
+                addAllInsideWay(dataset,w,newWays,newNodes);
+            }
+        }
+        if (!selectedRels.isEmpty()) {
+            for (Relation r: selectedRels) {
+                addAllInsideMultipolygon(dataset,r,newWays,newNodes);
+            }
+        }
+        
+        Set<OsmPrimitive> insideSelection = new HashSet<OsmPrimitive>();
+        if (!newWays.isEmpty() || !newNodes.isEmpty()) {
+            insideSelection.addAll(newWays);
+            insideSelection.addAll(newNodes);
+        }
+        return insideSelection;
+    }
+
+
 }
Index: src/utilsplugin2/dumbutils/ReplaceGeometryAction.java
===================================================================
--- src/utilsplugin2/dumbutils/ReplaceGeometryAction.java	(revision 27505)
+++ src/utilsplugin2/dumbutils/ReplaceGeometryAction.java	(working copy)
@@ -12,8 +12,10 @@
 import org.openstreetmap.josm.tools.Shortcut;
 import java.awt.event.ActionEvent;
 import org.openstreetmap.josm.actions.JosmAction;
-import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.*;
 import org.openstreetmap.josm.gui.DefaultNameFormatter;
+import org.openstreetmap.josm.gui.conflict.tags.CombinePrimitiveResolverDialog;
+import org.openstreetmap.josm.gui.conflict.tags.TagConflictResolutionUtil;
 import static org.openstreetmap.josm.tools.I18n.tr;
 
 /**
@@ -62,7 +64,7 @@
             return;
         }
     }
-    
+
     public void replaceNodeWithWay(Node node, Way way) {
         if (!node.getReferrers().isEmpty()) {
             JOptionPane.showMessageDialog(Main.parent, tr("Node has referrers, cannot replace with way."), TITLE, JOptionPane.INFORMATION_MESSAGE);
@@ -75,23 +77,31 @@
             Collection<Node> nodePool = getUnimportantNodes(way);
             nodeToReplace = findNearestNode(node, nodePool);
         }
-        
+
         List<Command> commands = new ArrayList<Command>();
         AbstractMap<String, String> nodeTags = (AbstractMap<String, String>) node.getKeys();
-        
+
+        // merge tags
+        Collection<Command> tagResolutionCommands = getTagConflictResolutionCommands(node, way);
+        if (tagResolutionCommands == null) {
+            // user canceled tag merge dialog
+            return;
+        }
+        commands.addAll(tagResolutionCommands);
+
         // replace sacrificial node in way with node that is being upgraded
         if (nodeToReplace != null) {
             List<Node> wayNodes = way.getNodes();
             int idx = wayNodes.indexOf(nodeToReplace);
             wayNodes.set(idx, node);
-            if (idx == 0) {
+            if (idx == 0 && way.isClosed()) {
                 // node is at start/end of way
                 wayNodes.set(wayNodes.size() - 1, node);
             }
             commands.add(new ChangeNodesCommand(way, wayNodes));
             commands.add(new MoveCommand(node, nodeToReplace.getCoor()));
             commands.add(new DeleteCommand(nodeToReplace));
-            
+
             // delete tags from node
             if (!nodeTags.isEmpty()) {
                 AbstractMap<String, String> nodeTagsToDelete = new HashMap<String, String>();
@@ -105,17 +115,13 @@
             commands.add(new DeleteCommand(node));
         }
 
-        // Copy tags from node
-        // TODO: use merge tag conflict dialog instead
-        commands.add(new ChangePropertyCommand(Arrays.asList(way), nodeTags));
-        
         getCurrentDataSet().setSelected(way);
-        
+
         Main.main.undoRedo.add(new SequenceCommand(
                 tr("Replace geometry for way {0}", way.getDisplayName(DefaultNameFormatter.getInstance())),
                 commands));
     }
-    
+
     public void replaceWayWithWay(List<Way> selection) {
         boolean overrideNewCheck = false;
         int idxNew = selection.get(0).isNew() ? 0 : 1;
@@ -186,9 +192,8 @@
         for( Node node : nodeAssoc.keySet() )
             commands.add(new MoveCommand(nodeAssoc.get(node), node.getCoor()));
 
-        // Copy tags from temporary way (source etc.)
-        for( String key : geometry.keySet() )
-            commands.add(new ChangePropertyCommand(way, key, geometry.get(key)));
+        // Merge tags from source to target way
+        commands.addAll(getTagConflictResolutionCommands(geometry, way));
 
         // Remove geometry way from selection
         getCurrentDataSet().clearSelection(geometry);
@@ -208,8 +213,9 @@
 
     /**
      * Create a list of nodes that are not used anywhere except in the way.
+     *
      * @param way
-     * @return 
+     * @return
      */
     public Collection<Node> getUnimportantNodes(Way way) {
         Set<Node> nodePool = new HashSet<Node>();
@@ -223,15 +229,53 @@
         }
         return nodePool;
     }
-    
+
     /**
+     * Merge tags from source to target object, showing resolution dialog if
+     * needed.
+     *
+     * @param source
+     * @param target
+     * @return
+     */
+    public List<Command> getTagConflictResolutionCommands(OsmPrimitive source, OsmPrimitive target) {
+        Collection<OsmPrimitive> primitives = Arrays.asList(source, target);
+        
+        Set<RelationToChildReference> relationToNodeReferences = RelationToChildReference.getRelationToChildReferences(primitives);
+
+        // build the tag collection
+        TagCollection tags = TagCollection.unionOfAllPrimitives(primitives);
+        TagConflictResolutionUtil.combineTigerTags(tags);
+        TagConflictResolutionUtil.normalizeTagCollectionBeforeEditing(tags, primitives);
+        TagCollection tagsToEdit = new TagCollection(tags);
+        TagConflictResolutionUtil.completeTagCollectionForEditing(tagsToEdit);
+
+        // launch a conflict resolution dialog, if necessary
+        CombinePrimitiveResolverDialog dialog = CombinePrimitiveResolverDialog.getInstance();
+        dialog.getTagConflictResolverModel().populate(tagsToEdit, tags.getKeysWithMultipleValues());
+        dialog.getRelationMemberConflictResolverModel().populate(relationToNodeReferences);
+        dialog.setTargetPrimitive(target);
+        dialog.prepareDefaultDecisions();
+
+        // conflict resolution is necessary if there are conflicts in the merged tags
+        // or if at least one of the merged nodes is referred to by a relation
+        if (!tags.isApplicableToPrimitive() || relationToNodeReferences.size() > 1) {
+            dialog.setVisible(true);
+            if (dialog.isCanceled()) {
+                return null;
+            }
+        }
+        return dialog.buildResolutionCommands();
+    }
+
+    /**
      * Find node from the collection which is nearest to <tt>node</tt>. Max distance is taken in consideration.
      * @return null if there is no such node.
      */
     private Node findNearestNode( Node node, Collection<Node> nodes ) {
         if( nodes.contains(node) )
             return node;
-        
+
         Node nearest = null;
         double distance = MAX_NODE_REPLACEMENT_DISTANCE;
         Point2D coor = node.getCoor();
