Changeset 2645 in josm for trunk/src


Ignore:
Timestamp:
2009-12-16T21:27:28+01:00 (12 years ago)
Author:
jttt
Message:

SearchCompiler refactoring, use search pattern for OsmPrimitive.hasDirectionKeys(), added toString() and type to dataset events

Location:
trunk/src/org/openstreetmap/josm
Files:
13 edited

Legend:

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

    r1641 r2645  
    33
    44import java.io.IOException;
    5 import java.io.PushbackReader;
    6 import java.util.LinkedList;
     5import java.io.Reader;
     6
     7import org.openstreetmap.josm.actions.search.SearchCompiler.ParseError;
    78
    89public class PushbackTokenizer {
    9     private PushbackReader search;
     10    private final Reader search;
    1011
    11     private LinkedList<String> pushBackBuf = new LinkedList<String>();
     12    private Token currentToken;
     13    private String currentText;
     14    private int c;
    1215
    13     public PushbackTokenizer(PushbackReader search) {
     16    public PushbackTokenizer(Reader search) {
    1417        this.search = search;
     18        getChar();
     19    }
     20
     21    public enum Token {NOT, OR, LEFT_PARENT, RIGHT_PARENT, COLON, EQUALS, KEY, EOF}
     22
     23    private void getChar() {
     24        try {
     25            c = search.read();
     26        } catch (IOException e) {
     27            throw new RuntimeException(e.getMessage(), e);
     28        }
    1529    }
    1630
     
    2337     * @return The next token in the stream.
    2438     */
    25     public String nextToken() {
    26         if (!pushBackBuf.isEmpty()) {
    27             return pushBackBuf.removeLast();
     39    public Token nextToken() {
     40        if (currentToken != null) {
     41            Token result = currentToken;
     42            currentToken = null;
     43            return result;
    2844        }
    2945
    30         try {
    31             int next;
    32             char c = ' ';
    33             while (c == ' ' || c == '\t' || c == '\n') {
    34                 next = search.read();
    35                 if (next == -1)
    36                     return null;
    37                 c = (char)next;
     46        while (Character.isWhitespace(c)) {
     47            getChar();
     48        }
     49        switch (c) {
     50        case -1:
     51            getChar();
     52            return Token.EOF;
     53        case ':':
     54            getChar();
     55            return Token.COLON;
     56        case '=':
     57            getChar();
     58            return Token.EQUALS;
     59        case '-':
     60            getChar();
     61            return Token.NOT;
     62        case '(':
     63            getChar();
     64            return Token.LEFT_PARENT;
     65        case ')':
     66            getChar();
     67            return Token.RIGHT_PARENT;
     68        case '|':
     69            getChar();
     70            return Token.OR;
     71        case '"':
     72        {
     73            StringBuilder s = new StringBuilder();
     74            while (c != -1 && c != '"') {
     75                s.append((char)c);
     76                getChar();
    3877            }
    39             StringBuilder s;
    40             switch (c) {
    41             case ':':
    42             case '=':
    43                 next = search.read();
    44                 if (next == -1 || next == ' ' || next == '\t') {
    45                     pushBack(" ");
    46                 } else {
    47                     search.unread(next);
    48                 }
    49                 return String.valueOf(c);
    50             case '-':
    51                 return "-";
    52             case '(':
    53                 return "(";
    54             case ')':
    55                 return ")";
    56             case '|':
    57                 return "|";
    58             case '"':
    59                 s = new StringBuilder(" ");
    60                 for (int nc = search.read(); nc != -1 && nc != '"'; nc = search.read())
    61                     s.append((char)nc);
    62                 return s.toString();
    63             default:
    64                 s = new StringBuilder();
    65             for (;;) {
    66                 s.append(c);
    67                 next = search.read();
    68                 if (next == -1) {
    69                     if (s.toString().equals("OR"))
    70                         return "|";
    71                     return " "+s.toString();
    72                 }
    73                 c = (char)next;
    74                 if (c == ' ' || c == '\t' || c == '"' || c == ':' || c == '(' || c == ')' || c == '|' || c == '=') {
    75                     search.unread(next);
    76                     if (s.toString().equals("OR"))
    77                         return "|";
    78                     return " "+s.toString();
    79                 }
     78            getChar();
     79            currentText = s.toString();
     80            return Token.KEY;
     81        }
     82        default:
     83        {
     84            StringBuilder s = new StringBuilder();
     85            while (!(c == -1 || Character.isWhitespace(c) || c == '"'|| c == ':' || c == '(' || c == ')' || c == '|' || c == '=')) {
     86                s.append((char)c);
     87                getChar();
    8088            }
    81             }
    82         } catch (IOException e) {
    83             throw new RuntimeException(e.getMessage(), e);
     89            currentText = s.toString();
     90            if ("or".equals(currentText))
     91                return Token.OR;
     92            else
     93                return Token.KEY;
     94        }
    8495        }
    8596    }
    8697
    87     public boolean readIfEqual(String tok) {
    88         String nextTok = nextToken();
    89         if (nextTok == null ? tok == null : nextTok.equals(tok))
     98    public boolean readIfEqual(Token token) {
     99        Token nextTok = nextToken();
     100        if (nextTok == null ? token == null : nextTok == token)
    90101            return true;
    91         pushBack(nextTok);
     102        currentToken = nextTok;
    92103        return false;
    93104    }
    94105
    95106    public String readText() {
    96         String nextTok = nextToken();
    97         if (nextTok != null && nextTok.startsWith(" "))
    98             return nextTok.substring(1);
    99         pushBack(nextTok);
     107        Token nextTok = nextToken();
     108        if (nextTok == Token.KEY)
     109            return currentText;
     110        currentToken = nextTok;
    100111        return null;
    101112    }
    102113
    103     public void pushBack(String tok) {
    104         pushBackBuf.addLast(tok);
     114    public String readText(String errorMessage) throws ParseError {
     115        String text = readText();
     116        if (text == null)
     117            throw new ParseError(errorMessage);
     118        else
     119            return text;
     120    }
     121
     122    public String getText() {
     123        return currentText;
    105124    }
    106125}
  • trunk/src/org/openstreetmap/josm/actions/search/SearchCompiler.java

    r2636 r2645  
    1414
    1515import org.openstreetmap.josm.Main;
     16import org.openstreetmap.josm.actions.search.PushbackTokenizer.Token;
    1617import org.openstreetmap.josm.data.osm.Node;
    1718import org.openstreetmap.josm.data.osm.OsmPrimitive;
     
    2324
    2425/**
    25  * Implements a google-like search.
    26  * @author Imi
     26 Implements a google-like search.
     27 <br>
     28 Grammar:
     29<pre>
     30expression =
     31  fact | expression
     32  fact expression
     33  fact
     34
     35fact =
     36 ( expression )
     37 -fact
     38 term=term
     39 term:term
     40 term
     41 </pre>
     42
     43 @author Imi
    2744 */
    2845public class SearchCompiler {
     
    184201    }
    185202
    186     private static class ExactKeyValue extends Match {
     203    public static class ExactKeyValue extends Match {
    187204
    188205        private enum Mode {
     
    201218                throw new ParseError(tr("Key cannot be empty when tag operator is used. Sample use: key=value"));
    202219            this.key = key;
    203             this.value = value;
     220            this.value = value == null?"":value;
    204221            if ("".equals(value) && "*".equals(key)) {
    205222                mode = Mode.NONE;
     
    527544            } else if (osm instanceof Relation) {
    528545                for (RelationMember member : ((Relation)osm).getMembers()) {
    529                     if (member.getMember() != null) {
    530                         // TODO Nullable member will not be allowed
    531                         isParent |= child.match(member.getMember());
    532                     }
     546                    isParent |= child.match(member.getMember());
    533547                }
    534548            }
     
    576590
    577591    public Match parse() throws ParseError {
    578         Match m = parseJuxta();
    579         if (!tokenizer.readIfEqual(null))
     592        Match m = parseExpression();
     593        if (!tokenizer.readIfEqual(Token.EOF))
    580594            throw new ParseError(tr("Unexpected token: {0}", tokenizer.nextToken()));
    581595        return m;
    582596    }
    583597
    584     private Match parseJuxta() throws ParseError {
    585         Match juxta = new Always();
    586 
    587         Match m;
    588         while ((m = parseOr()) != null) {
    589             juxta = new And(m, juxta);
    590         }
    591 
    592         return juxta;
    593     }
    594 
    595     private Match parseOr() throws ParseError {
    596         Match a = parseNot();
    597         if (tokenizer.readIfEqual("|")) {
    598             Match b = parseNot();
    599             if (a == null || b == null)
    600                 throw new ParseError(tr("Missing arguments for or."));
    601             return new Or(a, b);
    602         }
    603         return a;
    604     }
    605 
    606     private Match parseNot() throws ParseError {
    607         if (tokenizer.readIfEqual("-")) {
    608             Match m = parseParens();
    609             if (m == null)
    610                 throw new ParseError(tr("Missing argument for not."));
    611             return new Not(m);
    612         }
    613         return parseParens();
    614     }
    615 
    616     private Match parseParens() throws ParseError {
    617         if (tokenizer.readIfEqual("(")) {
    618             Match m = parseJuxta();
    619             if (!tokenizer.readIfEqual(")"))
    620                 throw new ParseError(tr("Expected closing parenthesis."));
    621             return m;
    622         }
    623         return parsePat();
    624     }
    625 
    626     private Match parsePat() throws ParseError {
    627         String tok = tokenizer.readText();
    628 
    629         if (tokenizer.readIfEqual(":")) {
    630             String tok2 = tokenizer.readText();
    631             if (tok == null) {
    632                 tok = "";
    633             }
    634             if (tok2 == null) {
    635                 tok2 = "";
    636             }
    637             return parseKV(tok, tok2);
    638         }
    639 
    640         if (tokenizer.readIfEqual("=")) {
    641             String tok2 = tokenizer.readText();
    642             if (tok == null) {
    643                 tok = "";
    644             }
    645             if (tok2 == null) {
    646                 tok2 = "";
    647             }
    648             return new ExactKeyValue(regexSearch, tok, tok2);
    649         }
    650 
    651         if (tok == null)
     598    private Match parseExpression() throws ParseError {
     599        Match factor = parseFactor();
     600        if (factor == null)
    652601            return null;
    653         else if (tok.equals("modified"))
    654             return new Modified();
    655         else if (tok.equals("incomplete"))
    656             return new Incomplete();
    657         else if (tok.equals("untagged"))
    658             return new Untagged();
    659         else if (tok.equals("selected"))
    660             return new Selected();
    661         else if (tok.equals("child"))
    662             return new Child(parseParens());
    663         else if (tok.equals("parent"))
    664             return new Parent(parseParens());
     602        if (tokenizer.readIfEqual(Token.OR))
     603            return new Or(factor, parseExpression(tr("Missing parameter for OR")));
     604        else {
     605            Match expression = parseExpression();
     606            if (expression == null)
     607                return factor;
     608            else
     609                return new And(factor, expression);
     610        }
     611    }
     612
     613    private Match parseExpression(String errorMessage) throws ParseError {
     614        Match expression = parseExpression();
     615        if (expression == null)
     616            throw new ParseError(errorMessage);
    665617        else
    666             return new Any(tok, regexSearch, caseSensitive);
    667     }
     618            return expression;
     619    }
     620
     621    private Match parseFactor() throws ParseError {
     622        if (tokenizer.readIfEqual(Token.LEFT_PARENT)) {
     623            Match expression = parseExpression();
     624            if (!tokenizer.readIfEqual(Token.RIGHT_PARENT))
     625                throw new ParseError(tr("Missing right parent"));
     626            return expression;
     627        } else if (tokenizer.readIfEqual(Token.NOT))
     628            return new Not(parseFactor(tr("Missing operator for NOT")));
     629        else if (tokenizer.readIfEqual(Token.KEY)) {
     630            String key = tokenizer.getText();
     631            if (tokenizer.readIfEqual(Token.EQUALS))
     632                return new ExactKeyValue(regexSearch, key, tokenizer.readText());
     633            else if (tokenizer.readIfEqual(Token.COLON))
     634                return parseKV(key, tokenizer.readText());
     635            else if ("modified".equals(key))
     636                return new Modified();
     637            else if ("incomplete".equals(key))
     638                return new Incomplete();
     639            else if ("untagged".equals(key))
     640                return new Untagged();
     641            else if ("selected".equals(key))
     642                return new Selected();
     643            else if ("child".equals(key))
     644                return new Child(parseFactor());
     645            else if ("parent".equals(key))
     646                return new Parent(parseFactor());
     647            else
     648                return new Any(key, regexSearch, caseSensitive);
     649        } else
     650            return null;
     651    }
     652
     653    private Match parseFactor(String errorMessage) throws ParseError {
     654        Match fact = parseFactor();
     655        if (fact == null)
     656            throw new ParseError(errorMessage);
     657        else
     658            return fact;
     659    }
     660
     661
    668662
    669663    private Match parseKV(String key, String value) throws ParseError {
     664        if (value == null) {
     665            value = "";
     666        }
    670667        if (key.equals("type"))
    671668            return new ExactType(value);
  • trunk/src/org/openstreetmap/josm/data/Preferences.java

    r2641 r2645  
    670670    }
    671671
     672    public boolean isCollection(String key, boolean def) {
     673        String s = get(key);
     674        if (s != null && s.length() != 0)
     675            return s.indexOf("\u001e") >= 0 || s.indexOf("§§§") >= 0;
     676            else
     677                return def;
     678    }
     679
    672680    synchronized public Collection<String> getCollection(String key, Collection<String> def) {
    673681        String s = get(key);
  • trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java

    r2623 r2645  
    2121
    2222import org.openstreetmap.josm.Main;
     23import org.openstreetmap.josm.actions.search.SearchCompiler;
     24import org.openstreetmap.josm.actions.search.SearchCompiler.Match;
     25import org.openstreetmap.josm.actions.search.SearchCompiler.ParseError;
    2326import org.openstreetmap.josm.data.osm.visitor.Visitor;
    2427import org.openstreetmap.josm.gui.mappaint.ElemStyle;
     
    512515    private int timestamp;
    513516
    514     private static Collection<String> uninteresting = null;
     517    private static volatile Collection<String> uninteresting = null;
    515518    /**
    516519     * Contains a list of "uninteresting" keys that do not make an object
     
    526529    }
    527530
    528     private static Collection<String> directionKeys = null;
     531    private static volatile Match directionKeys = null;
    529532
    530533    /**
     
    533536     * Initialized by checkDirectionTagged()
    534537     */
    535     public static Collection<String> getDirectionKeys() {
     538    public static void initDirectionKeys() {
    536539        if(directionKeys == null) {
    537             directionKeys = Main.pref.getCollection("tags.direction",
    538                     Arrays.asList("oneway","incline","incline_steep","aerialway"));
    539         }
    540         return directionKeys;
     540
     541            // Legacy support - convert list of keys to search pattern
     542            if (Main.pref.isCollection("tags.direction", false)) {
     543                System.out.println("Collection of keys in tags.direction is no longer supported, value will converted to search pattern");
     544                Collection<String> keys = Main.pref.getCollection("tags.direction", null);
     545                StringBuilder builder = new StringBuilder();
     546                for (String key:keys) {
     547                    builder.append(key);
     548                    builder.append("=* | ");
     549                }
     550                builder.delete(builder.length() - 3, builder.length());
     551                Main.pref.put("tags.direction", builder.toString());
     552            }
     553
     554            String defaultValue = "oneway=* | incline=* | incline_steep=* | aerialway=*";
     555            try {
     556                directionKeys = SearchCompiler.compile(Main.pref.get("tags.direction", defaultValue), false, false);
     557            } catch (ParseError e) {
     558                System.err.println("Unable to compile pattern for tags.direction, trying default pattern: " + e.getMessage());
     559
     560                try {
     561                    directionKeys = SearchCompiler.compile(defaultValue, false, false);
     562                } catch (ParseError e2) {
     563                    throw new AssertionError("Unable to compile default pattern for direction keys: " + e2.getMessage());
     564                }
     565            }
     566        }
    541567    }
    542568
     
    10711097
    10721098    private void updateHasDirectionKeys() {
    1073         getDirectionKeys();
    1074         if (keys != null) {
    1075             for (Entry<String,String> e : getKeys().entrySet()) {
    1076                 if (directionKeys.contains(e.getKey())) {
    1077                     flags |= FLAG_HAS_DIRECTIONS;
    1078                     return;
    1079                 }
    1080             }
    1081         }
    1082         flags &= ~FLAG_HAS_DIRECTIONS;
     1099        initDirectionKeys();
     1100        if (directionKeys.match(this)) {
     1101            flags |= FLAG_HAS_DIRECTIONS;
     1102        } else {
     1103            flags &= ~FLAG_HAS_DIRECTIONS;
     1104        }
    10831105    }
    10841106    /**
  • trunk/src/org/openstreetmap/josm/data/osm/event/AbstractDatasetChangedEvent.java

    r2622 r2645  
    1010public abstract class AbstractDatasetChangedEvent {
    1111
     12    public enum DatasetEventType {DATA_CHANGED, NODE_MOVED, PRIMITIVES_ADDED, PRIMITIVES_REMOVED,
     13        RELATION_MEMBERS_CHANGED, TAGS_CHANGED, WAY_NODES_CHANGED}
     14
    1215    protected final DataSet dataSet;
    1316
     
    1720
    1821    public abstract void fire(DataSetListener listener);
     22
     23    /**
     24     * Returns list of primitives modified by this event.
     25     * <br/>
     26     * <strong>WARNING</strong> This value might be incorrect in case
     27     * of {@link DataChangedEvent}. It returns all primitives in the dataset
     28     * when this method is called (live list), not list of primitives when
     29     * the event was created
     30     * @return List of modified primitives
     31     */
    1932    public abstract List<? extends OsmPrimitive> getPrimitives();
    2033
     
    2336    }
    2437
     38    public abstract DatasetEventType getType();
     39
     40    @Override
     41    public String toString() {
     42        return getType().toString();
     43    }
     44
    2545}
  • trunk/src/org/openstreetmap/josm/data/osm/event/DataChangedEvent.java

    r2622 r2645  
    2424    }
    2525
     26    @Override
     27    public DatasetEventType getType() {
     28        return DatasetEventType.DATA_CHANGED;
     29    }
     30
    2631}
  • trunk/src/org/openstreetmap/josm/data/osm/event/DataSetListenerAdapter.java

    r2622 r2645  
    22package org.openstreetmap.josm.data.osm.event;
    33
     4/**
     5 * Classes that do not wish to implement all methods of DataSetListener
     6 * may use this class. Implement DatasetListenerAdapter.Listener and
     7 * pass this adapter instead of class itself.
     8 *
     9 */
    410public class DataSetListenerAdapter implements DataSetListener {
    511
  • trunk/src/org/openstreetmap/josm/data/osm/event/NodeMovedEvent.java

    r2622 r2645  
    3232    }
    3333
     34    @Override
     35    public DatasetEventType getType() {
     36        return DatasetEventType.NODE_MOVED;
     37    }
     38
    3439}
  • trunk/src/org/openstreetmap/josm/data/osm/event/PrimitivesAddedEvent.java

    r2623 r2645  
    3939    }
    4040
     41    @Override
     42    public DatasetEventType getType() {
     43        return DatasetEventType.PRIMITIVES_ADDED;
     44    }
     45
    4146}
  • trunk/src/org/openstreetmap/josm/data/osm/event/PrimitivesRemovedEvent.java

    r2623 r2645  
    3939    }
    4040
     41    @Override
     42    public DatasetEventType getType() {
     43        return DatasetEventType.PRIMITIVES_REMOVED;
     44    }
     45
    4146}
  • trunk/src/org/openstreetmap/josm/data/osm/event/RelationMembersChangedEvent.java

    r2622 r2645  
    3232    }
    3333
     34    @Override
     35    public DatasetEventType getType() {
     36        return DatasetEventType.RELATION_MEMBERS_CHANGED;
     37    }
     38
    3439}
  • trunk/src/org/openstreetmap/josm/data/osm/event/TagsChangedEvent.java

    r2622 r2645  
    3131    }
    3232
     33    @Override
     34    public DatasetEventType getType() {
     35        return DatasetEventType.TAGS_CHANGED;
     36    }
     37
    3338}
  • trunk/src/org/openstreetmap/josm/data/osm/event/WayNodesChangedEvent.java

    r2622 r2645  
    3232    }
    3333
     34    @Override
     35    public DatasetEventType getType() {
     36        return DatasetEventType.WAY_NODES_CHANGED;
     37    }
     38
    3439}
Note: See TracChangeset for help on using the changeset viewer.