Changeset 513 in josm for trunk/src/org/openstreetmap


Ignore:
Timestamp:
2008-01-08T15:02:34+01:00 (17 years ago)
Author:
gebner
Message:

Implement a recursive-descent parser for SearchCompiler.

Location:
trunk/src/org/openstreetmap/josm/actions/search
Files:
1 added
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/actions/search/SearchCompiler.java

    r343 r513  
    66import java.io.StringReader;
    77import java.util.Map.Entry;
     8import java.util.regex.Pattern;
     9import java.util.regex.Matcher;
    810
    911import org.openstreetmap.josm.data.osm.Node;
     
    1820public class SearchCompiler {
    1921
    20         boolean caseSensitive = false;
     22        private boolean caseSensitive = false;
     23        private PushbackTokenizer tokenizer;
     24
     25        public SearchCompiler(boolean caseSensitive, PushbackTokenizer tokenizer) {
     26                this.caseSensitive = caseSensitive;
     27                this.tokenizer = tokenizer;
     28        }
    2129       
    2230        abstract public static class Match {
     
    7179                private String key;
    7280                private String value;
    73                 boolean notValue;
    74                 public KeyValue(String key, String value, boolean notValue) {this.key = key; this.value = value; this.notValue = notValue;}
     81                public KeyValue(String key, String value) {this.key = key; this.value = value; }
    7582                @Override public boolean match(OsmPrimitive osm) {
    7683                        String value = null;
     
    8087                                value = osm.get(key);
    8188                        if (value == null)
    82                                 return notValue;
     89                                return false;
    8390                        String v1 = caseSensitive ? value : value.toLowerCase();
    8491                        String v2 = caseSensitive ? this.value : this.value.toLowerCase();
    85                         return (v1.indexOf(v2) != -1) != notValue;
    86                 }
    87                 @Override public String toString() {return key+"="+(notValue?"!":"")+value;}
     92                        return v1.indexOf(v2) != -1;
     93                }
     94                @Override public String toString() {return key+"="+value;}
    8895        }
    8996
     
    151158       
    152159        public static Match compile(String searchStr, boolean caseSensitive) {
    153                 SearchCompiler searchCompiler = new SearchCompiler();
    154                 searchCompiler.caseSensitive = caseSensitive;
    155                 return searchCompiler.parse(new PushbackReader(new StringReader(searchStr)));
    156         }
    157 
    158 
    159         /**
    160          * The token returned is <code>null</code> or starts with an identifier character:
    161          * - for an '-'. This will be the only character
    162          * : for an key. The value is the next token
    163          * | for "OR"
    164          * ' ' for anything else.
    165          * @return The next token in the stream.
    166          */
    167         private String nextToken(PushbackReader search) {
    168                 try {
    169                         int next;
    170                         char c = ' ';
    171                         while (c == ' ' || c == '\t' || c == '\n') {
    172                                 next = search.read();
    173                                 if (next == -1)
    174                                         return null;
    175                                 c = (char)next;
    176                         }
    177                         StringBuilder s;
    178                         switch (c) {
    179                         case '-':
    180                                 return "-";
    181                         case '"':
    182                                 s = new StringBuilder(" ");
    183                                 for (int nc = search.read(); nc != -1 && nc != '"'; nc = search.read())
    184                                         s.append((char)nc);
    185                                 int nc = search.read();
    186                                 if (nc != -1 && (char)nc == ':')
    187                                         return ":"+s.toString();
    188                                 if (nc != -1)
    189                                         search.unread(nc);
    190                                 return s.toString();
    191                         default:
    192                                 s = new StringBuilder();
    193                         for (;;) {
    194                                 s.append(c);
    195                                 next = search.read();
    196                                 if (next == -1) {
    197                                         if (s.toString().equals("OR"))
    198                                                 return "|";
    199                                         return " "+s.toString();
    200                                 }
    201                                 c = (char)next;
    202                                 if (c == ' ' || c == '\t' || c == ':' || c == '"') {
    203                                         if (c == ':')
    204                                                 return ":"+s.toString();
    205                                         search.unread(next);
    206                                         if (s.toString().equals("OR"))
    207                                                 return "|";
    208                                         return " "+s.toString();
    209                                 }
    210                         }
    211                         }
    212                 } catch (IOException e) {
    213                         throw new RuntimeException(e.getMessage(), e);
    214                 }               
    215         }
    216 
    217 
    218         private boolean notKey = false;
    219         private boolean notValue = false;
    220         private boolean or = false;
    221         private String key = null;
    222         String token = null;
    223         private Match build() {
    224                 String value = token.substring(1);
    225                 if (key == null) {
    226                         Match c = null;
    227                         if (value.equals("modified"))
    228                                 c = new Modified();
    229                         else if (value.equals("incomplete"))
    230                                 c = new Incomplete();
    231                         else if (value.equals("selected"))
    232                                 c = new Selected();
    233                         else
    234                                 c = new Any(value);
    235                         return notValue ? new Not(c) : c;
    236                 }
    237                 Match c;
    238                 if (key.equals("type"))
    239                         c = new ExactType(value);
    240                 else if (key.equals("property")) {
    241                         String realKey = "", realValue = value;
    242                         int eqPos = value.indexOf("=");
    243                         if (eqPos != -1) {
    244                                 realKey = value.substring(0,eqPos);
    245                                 realValue = value.substring(eqPos+1);
    246                         }
    247                         c = new KeyValue(realKey, realValue, notValue);
     160                return new SearchCompiler(caseSensitive,
     161                                new PushbackTokenizer(
     162                                        new PushbackReader(new StringReader(searchStr))))
     163                        .parse();
     164        }
     165
     166        public Match parse() {
     167                Match m = parseJuxta();
     168                if (!tokenizer.readIfEqual(null)) {
     169                        throw new RuntimeException("Unexpected token: " + tokenizer.nextToken());
     170                }
     171                return m;
     172        }
     173
     174        private Match parseJuxta() {
     175                Match juxta = new Always();
     176
     177                Match m;
     178                while ((m = parseOr()) != null) {
     179                        juxta = new And(m, juxta);
     180                }
     181
     182                return juxta;
     183        }
     184
     185        private Match parseOr() {
     186                Match a = parseNot();
     187                if (tokenizer.readIfEqual("|")) {
     188                        Match b = parseNot();
     189                        if (a == null || b == null) {
     190                                throw new RuntimeException("Missing arguments for or.");
     191                        }
     192                        return new Or(a, b);
     193                }
     194                return a;
     195        }
     196
     197        private Match parseNot() {
     198                if (tokenizer.readIfEqual("-")) {
     199                        Match m = parseParens();
     200                        if (m == null) {
     201                                throw new RuntimeException("Missing argument for not.");
     202                        }
     203                        return new Not(m);
     204                }
     205                return parseParens();
     206        }
     207
     208        private Match parseParens() {
     209                if (tokenizer.readIfEqual("(")) {
     210                        Match m = parseJuxta();
     211                        if (!tokenizer.readIfEqual(")")) {
     212                                throw new RuntimeException("Expected closing paren");
     213                        }
     214                        return m;
     215                }
     216                return parsePat();
     217        }
     218
     219        private Match parsePat() {
     220                String tok = tokenizer.readText();
     221
     222                if (tokenizer.readIfEqual(":")) {
     223                        String tok2 = tokenizer.readText();
     224                        if (tok == null) tok = "";
     225                        if (tok2 == null) tok2 = "";
     226                        return parseKV(tok, tok2);
     227                }
     228
     229                if (tok == null) {
     230                        return null;
     231                } else if (tok.equals("modified")) {
     232                        return new Modified();
     233                } else if (tok.equals("incomplete")) {
     234                        return new Incomplete();
     235                } else if (tok.equals("selected")) {
     236                        return new Selected();
     237                } else {
     238                        return new Any(tok);
     239                }
     240        }
     241
     242        private Match parseKV(String key, String value) {
     243                if (key.equals("type")) {
     244                        return new ExactType(value);
    248245                } else if (key.equals("id")) {
    249246                        try {
    250                                 c = new Id(Long.parseLong(value));
     247                                return new Id(Long.parseLong(value));
    251248                        } catch (NumberFormatException x) {
    252                                 c = new Id(0);
    253                         }
    254                         if (notValue)
    255                                 c = new Not(c);
    256                 } else
    257                         c = new KeyValue(key, value, notValue);
    258                 if (notKey)
    259                         return new Not(c);
    260                 return c;
    261         }
    262 
    263         private Match parse(PushbackReader search) {
    264                 Match result = null;
    265                 for (token = nextToken(search); token != null; token = nextToken(search)) {
    266                         if (token.equals("-"))
    267                                 notValue = true;
    268                         else if (token.equals("|")) {
    269                                 if (result == null)
    270                                         continue;
    271                                 or = true;
    272                                 notValue = false;
    273                         } else if (token.startsWith(":")) {
    274                                 if (key == null) {
    275                                         key = token.substring(1);
    276                                         notKey = notValue;
    277                                         notValue = false;
    278                                 } else
    279                                         key += token.substring(1);
    280                         } else {
    281                                 Match current = build();
    282                                 if (result == null)
    283                                         result = current;
    284                                 else
    285                                         result = or ? new Or(result, current) : new And(result, current);
    286                                         key = null;
    287                                         notKey = false;
    288                                         notValue = false;
    289                                         or = false;
    290                         }
    291                 }
    292                 // if "key:" was the last search
    293                 if (key != null) {
    294                         token = " ";
    295                         Match current = build();
    296                         result = (result == null) ? current : new And(result, current);
    297                 }
    298                 return result == null ? new Always() : result;
     249                                return new Id(0);
     250                        }
     251                } else {
     252                        return new KeyValue(key, value);
     253                }
    299254        }
    300255}
Note: See TracChangeset for help on using the changeset viewer.