Index: src/org/openstreetmap/josm/actions/search/SearchCompiler.java
===================================================================
--- src/org/openstreetmap/josm/actions/search/SearchCompiler.java	(revision 315)
+++ src/org/openstreetmap/josm/actions/search/SearchCompiler.java	(working copy)
@@ -5,11 +5,13 @@
 import java.io.PushbackReader;
 import java.io.StringReader;
 import java.util.Map.Entry;
+import java.util.Stack;
 
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.Segment;
 import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.osm.User;
 
 /**
  * Implements a google-like search.
@@ -18,6 +20,7 @@
 public class SearchCompiler {
 
 	boolean caseSensitive = false;
+	private static enum Operator { OpParen, OpOr, OpAnd, OpNot }; 
 	
 	abstract public static class Match {
 		abstract public boolean match(OsmPrimitive osm);
@@ -67,6 +70,15 @@
 		@Override public String toString() {return "id="+id;}
 	}
 
+	private static class UserMatch extends Match {
+		private User user;
+		public UserMatch(String username) {this.user = username.equals("")?null:User.get(username);}
+		@Override public boolean match(OsmPrimitive osm) {
+			return osm.user == user;
+		}
+		@Override public String toString() {return "user="+(user==null?"(null)":user.name);}
+	}
+
 	private class KeyValue extends Match {
 		private String key;
 		private String value;
@@ -150,7 +162,7 @@
 
 	/**
 	 * The token returned is <code>null</code> or starts with an identifier character:
-	 * - for an '-'. This will be the only character
+	 * - for an '-'. Similarly '(' and ')' are returned as is
 	 * : for an key. The value is the next token
 	 * | for "OR"
 	 * ' ' for anything else.
@@ -170,6 +182,10 @@
 			switch (c) {
 			case '-':
 				return "-";
+			case '(':
+				return "(";
+			case ')':
+				return ")";	
 			case '"':
 				s = new StringBuilder(" ");
 				for (int nc = search.read(); nc != -1 && nc != '"'; nc = search.read())
@@ -191,7 +207,7 @@
 					return " "+s.toString();
 				}
 				c = (char)next;
-				if (c == ' ' || c == '\t' || c == ':' || c == '"') {
+				if (c == ' ' || c == '\t' || c == ':' || c == '"' || c == '(' || c == ')' ) {
 					if (c == ':')
 						return ":"+s.toString();
 					search.unread(next);
@@ -207,11 +223,10 @@
 	}
 
 
-	private boolean notKey = false;
 	private boolean notValue = false;
-	private boolean or = false;
 	private String key = null;
 	String token = null;
+	/* Takes a key and a possible value and builds it into a match object */
 	private Match build() {
 		String value = token.substring(1);
 		if (key == null) {
@@ -227,9 +242,9 @@
 			return notValue ? new Not(c) : c;
 		}
 		Match c;
-		if (key.equals("type"))
+		if (key.equals("type")) {
 			c = new ExactType(value);
-		else if (key.equals("property")) {
+		} else if (key.equals("property")) {
 			String realKey = "", realValue = value;
 			int eqPos = value.indexOf("=");
 			if (eqPos != -1) {
@@ -237,56 +252,136 @@
 				realValue = value.substring(eqPos+1);
 			}
 			c = new KeyValue(realKey, realValue, notValue);
+			notValue = false;
 		} else if (key.equals("id")) {
 			try {
 				c = new Id(Long.parseLong(value));
 			} catch (NumberFormatException x) {
 				c = new Id(0);
 			}
-			if (notValue)
-				c = new Not(c);
-		} else
+		} else if (key.equals("user")) {
+			c = new UserMatch(value);
+		} else {
 			c = new KeyValue(key, value, notValue);
-		if (notKey)
-			return new Not(c);
+			notValue = false;
+		}
+		if (notValue)
+			c = new Not(c);
 		return c;
 	}
 
+	/* Stacks for managing the operator precedence */
+	private Stack<Operator> opstack;
+	private Stack<Match>    argstack;
+	/* Push the operator on the stack, but only after ensuring all giher precedence operators are applied */
+	private void pushOp(Operator op)
+	{
+		while( !opstack.empty() && opstack.peek().compareTo(op) > 0)
+			applyOp();
+		opstack.push(op);
+	}
+	/* A close parenthesis applies all operators until the open paren */
+	private void closeParen()
+	{
+		while( !opstack.empty() && opstack.peek() != Operator.OpParen)
+			applyOp();
+		if( opstack.empty() )
+			throw new Error( "Too many close parenthesis" );
+		opstack.pop();
+	}
+	private void applyOp()
+	{
+		try {
+			switch( opstack.pop() )
+			{
+				case OpAnd:
+					argstack.push( new And(argstack.pop(), argstack.pop()) );
+					break;
+				case OpOr:
+					argstack.push( new Or(argstack.pop(), argstack.pop()) );
+					break;
+				case OpNot:
+					argstack.push( new Not(argstack.pop()) );
+					break;
+				default:
+					throw new Error( "Unknown operator" );
+			}
+		}
+		catch( java.util.EmptyStackException e )
+		{
+			throw new Error("Missing argument");
+		}
+	}
+	/* The actual parser */
 	private Match parse(PushbackReader search) {
-		Match result = null;
+		argstack = new Stack<Match>();
+		opstack = new Stack<Operator>();
+		
+		pushOp( Operator.OpParen );
+		boolean hadArg = false;
 		for (token = nextToken(search); token != null; token = nextToken(search)) {
+//			System.out.println( "token:["+token+"], opstack:"+opstack+", argstack:"+argstack+", hadArg:"+hadArg );
 			if (token.equals("-"))
-				notValue = true;
-			else if (token.equals("|")) {
-				if (result == null)
-					continue;
-				or = true;
-				notValue = false;
-			} else if (token.startsWith(":")) {
+			{
+				if( hadArg )
+					pushOp(Operator.OpAnd);
+				hadArg = false;
+				if( key == null )
+					pushOp(Operator.OpNot);
+				else
+					notValue = true;
+			}
+			else if (token.equals("("))
+			{
+				if( hadArg )
+					pushOp(Operator.OpAnd);
+				hadArg = false;
+				opstack.push(Operator.OpParen);
+			}
+			else if (token.equals(")"))
+			{
+				hadArg = true;
+				closeParen();
+			}
+			else if (token.equals("|"))
+			{
+				pushOp(Operator.OpOr);
+				hadArg = false;
+			}
+			else if (token.startsWith(":")) {
+				if( hadArg )
+					pushOp(Operator.OpAnd);
+				hadArg = false;
 				if (key == null) {
 					key = token.substring(1);
-					notKey = notValue;
 					notValue = false;
 				} else
 					key += token.substring(1);
 			} else {
+				if( hadArg )
+					pushOp(Operator.OpAnd);
 				Match current = build();
-				if (result == null)
-					result = current;
-				else
-					result = or ? new Or(result, current) : new And(result, current);
-					key = null;
-					notKey = false;
-					notValue = false;
-					or = false;
-			}
+				argstack.push(current);
+				hadArg = true;
+				key = null;
+				notValue = false;
+			} 
 		}
+//		System.out.println( "End loop: opstack:"+opstack+", argstack:"+argstack+", hadArg:"+hadArg );
 		// if "key:" was the last search
 		if (key != null) {
+			if( hadArg )
+				pushOp(Operator.OpAnd);
 			token = " ";
 			Match current = build();
-			result = (result == null) ? current : new And(result, current);
+			argstack.push(current);
 		}
-		return result == null ? new Always() : result;
+		closeParen();
+//		System.out.println( "Finish: opstack:"+opstack+", argstack:"+argstack+", hadArg:"+hadArg );
+		if( !opstack.empty() || argstack.size() > 1 )
+			throw new Error("Parse error");
+		if( argstack.empty() )
+			return new Always();
+		return argstack.pop();
 	}
 }
Index: src/org/openstreetmap/josm/actions/search/SearchAction.java
===================================================================
--- src/org/openstreetmap/josm/actions/search/SearchAction.java	(revision 315)
+++ src/org/openstreetmap/josm/actions/search/SearchAction.java	(working copy)
@@ -85,8 +85,9 @@
     		}
     	}
     	Collection<OsmPrimitive> sel = Main.ds.getSelected();
-    	SearchCompiler.Match matcher = SearchCompiler.compile(search, caseSensitive);
-    	for (OsmPrimitive osm : Main.ds.allNonDeletedPrimitives()) {
+    	try {
+    	        SearchCompiler.Match matcher = SearchCompiler.compile(search, caseSensitive);
+        for (OsmPrimitive osm : Main.ds.allNonDeletedPrimitives()) {
     		if (mode == SearchMode.replace) {
     			if (matcher.match(osm))
     				sel.add(osm);
@@ -97,6 +98,10 @@
     		else if (mode == SearchMode.remove && osm.selected && matcher.match(osm))
     			sel.remove(osm);
     	}
+        } catch( Error e ) {
+    	        JOptionPane.showMessageDialog(Main.parent, tr("Error in expression"));
+    	        return;
+        }
     	Main.ds.setSelected(sel);
     }
 }
