Changeset 34820 in osm


Ignore:
Timestamp:
2019-01-13T14:19:42+01:00 (6 years ago)
Author:
gerdp
Message:

see #17201

  • Improve progress monitor
  • React on cancel button
  • fix checkstyle and findbugs issues and improve javadoc
Location:
applications/editors/josm/plugins/o5m/src/org/openstreetmap/josm/plugins/o5m
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • applications/editors/josm/plugins/o5m/src/org/openstreetmap/josm/plugins/o5m/O5mConstants.java

    r33012 r34820  
    77
    88/**
    9  *
     9 * Constants for o5m plugin.
    1010 * @author GerdP
    1111 *
  • applications/editors/josm/plugins/o5m/src/org/openstreetmap/josm/plugins/o5m/O5mPlugin.java

    r33012 r34820  
    99/**
    1010 *
     11 * o5m Plugin
    1112 * @author GerdP
    1213 *
  • applications/editors/josm/plugins/o5m/src/org/openstreetmap/josm/plugins/o5m/io/O5mImporter.java

    r33566 r34820  
    1414
    1515/**
     16 * OSM Importer for o5m format (*.o5m).
    1617 * @author GerdP
    1718 *
  • applications/editors/josm/plugins/o5m/src/org/openstreetmap/josm/plugins/o5m/io/O5mReader.java

    r34599 r34820  
    2929import org.openstreetmap.josm.io.AbstractReader;
    3030import org.openstreetmap.josm.io.IllegalDataException;
     31import org.openstreetmap.josm.io.ImportCancelException;
    3132import org.openstreetmap.josm.tools.CheckParameterUtil;
    3233import org.openstreetmap.josm.tools.Logging;
    3334
    3435/**
     36 * Read stream in o5m format.
    3537 * @author GerdP
    3638 *
     
    5860    }
    5961   
    60         // O5M data set constants
    61         private static final int NODE_DATASET = 0x10;
    62         private static final int WAY_DATASET = 0x11;
    63         private static final int REL_DATASET = 0x12;
    64         private static final int BBOX_DATASET = 0xdb;
    65         private static final int TIMESTAMP_DATASET = 0xdc;
    66         private static final int HEADER_DATASET = 0xe0;
    67         private static final int EOD_FLAG = 0xfe;
    68         private static final int RESET_FLAG = 0xff;
    69        
    70         private static final int EOF_FLAG = -1;
    71        
    72         // o5m constants
    73         private static final int STRING_TABLE_SIZE = 15000;
    74         private static final int MAX_STRING_PAIR_SIZE = 250 + 2;
    75         private static final String[] REL_REF_TYPES = {"node", "way", "relation", "?"};
    76         private static final double FACTOR = 1d/1000000000; // used with 100*<Val>*FACTOR
    77        
    78         private final BufferedInputStream fis;
    79         private InputStream is;
    80         private ByteArrayInputStream bis;
    81        
    82         // buffer for byte -> String conversions
    83         private byte[] cnvBuffer;
    84        
    85         private byte[] ioBuf;
    86         private int ioPos;
    87         // the o5m string table
    88         private String[][] stringTable;
    89         private String[] stringPair;
    90         private int currStringTablePos;
    91         // a counter that must be maintained by all routines that read data from the stream
    92         private int bytesToRead;
    93         // total number of bytes read from stream
    94         long countBytes;
    95 
    96         // for delta calculations
    97         private long lastNodeId;
    98         private long lastWayId;
    99         private long lastRelId;
    100         private long[] lastRef;
    101         private long lastTs;
    102         private long lastChangeSet;
    103         private int lastLon, lastLat;
    104         private int version;
    105         private User osmUser;
    106         private String header;
    107         /**
    108          * A parser for the o5m format
    109          * @param stream The InputStream that contains the OSM data in o5m format
    110          */
    111         O5mReader(InputStream stream) {
    112             this.fis = new BufferedInputStream(stream);
     62    // O5M data set constants
     63    private static final int NODE_DATASET = 0x10;
     64    private static final int WAY_DATASET = 0x11;
     65    private static final int REL_DATASET = 0x12;
     66    private static final int BBOX_DATASET = 0xdb;
     67    private static final int TIMESTAMP_DATASET = 0xdc;
     68    private static final int HEADER_DATASET = 0xe0;
     69    private static final int EOD_FLAG = 0xfe;
     70    private static final int RESET_FLAG = 0xff;
     71
     72    private static final int EOF_FLAG = -1;
     73
     74    // o5m constants
     75    private static final int STRING_TABLE_SIZE = 15000;
     76    private static final int MAX_STRING_PAIR_SIZE = 250 + 2;
     77    private static final String[] REL_REF_TYPES = {"node", "way", "relation", "?"};
     78    private static final double FACTOR = 1d/1000000000; // used with 100*<Val>*FACTOR
     79
     80    private BufferedInputStream fis;
     81    private InputStream is;
     82    private ByteArrayInputStream bis;
     83
     84    // buffer for byte -> String conversions
     85    private byte[] cnvBuffer;
     86
     87    private byte[] ioBuf;
     88    private int ioPos;
     89    // the o5m string table
     90    private String[][] stringTable;
     91    private String[] stringPair;
     92    private int currStringTablePos;
     93    // a counter that must be maintained by all routines that read data from the stream
     94    private int bytesToRead;
     95    // total number of bytes read from stream
     96    long countBytes;
     97
     98    // for delta calculations
     99    private long lastNodeId;
     100    private long lastWayId;
     101    private long lastRelId;
     102    private long[] lastRef;
     103    private long lastTs;
     104    private long lastChangeSet;
     105    private int lastLon, lastLat;
     106    private int version;
     107    private User osmUser;
     108    private String header;
     109    /**
     110     * A parser for the o5m format
     111     */
     112    O5mReader() {
     113        this.cnvBuffer = new byte[4000]; // OSM data should not contain string pairs with length > 512
     114        this.ioBuf = new byte[8192];
     115        this.ioPos = 0;
     116        this.stringPair = new String[2];
     117        this.lastRef = new long[3];
     118        reset();
     119    }
     120
     121    /**
     122     * parse the input stream
     123     * @param source The InputStream that contains the OSM data in o5m format
     124     * @throws O5mParsingCancelException if operation was canceled
     125     */
     126    public void parse(InputStream source) throws O5mParsingCancelException {
     127        this.fis = new BufferedInputStream(source);
     128        is = fis;
     129
     130        try {
     131            int start = is.read();
     132            ++countBytes;
     133            if (start != RESET_FLAG)
     134                throw new IOException(tr("wrong header byte ") + Integer.toHexString(start));
     135            readFile();
     136            if (discourageUpload)
     137                ds.setUploadPolicy(UploadPolicy.DISCOURAGED);
     138        } catch (IOException e) {
     139            Logging.error(e);
     140        }
     141    }
     142
     143    private void readFile() throws IOException, O5mParsingCancelException {
     144        boolean done = false;
     145        while (!done) {
     146            if (cancel) {
     147                cancel = false;
     148                throw new O5mParsingCancelException(tr("Reading was canceled at file offset {0}", countBytes));
     149            }
    113150            is = fis;
    114             this.cnvBuffer = new byte[4000]; // OSM data should not contain string pairs with length > 512
    115             this.ioBuf = new byte[8192];
    116             this.ioPos = 0;
    117             this.stringPair = new String[2];
    118             this.lastRef = new long[3];
    119             reset();
    120         }
    121 
    122         /**
    123          * parse the input stream
    124          */
    125         public void parse() {
    126             try {
    127                 int start = is.read();
    128                 ++countBytes;
    129                 if (start != RESET_FLAG)
    130                     throw new IOException(tr("wrong header byte ") + Integer.toHexString(start));
    131                 readFile();
    132                 if (discourageUpload)
    133                     ds.setUploadPolicy(UploadPolicy.DISCOURAGED);
    134             } catch (IOException e) {
    135                 Logging.error(e);
    136             }
    137         }
    138        
    139         private void readFile() throws IOException {
    140             boolean done = false;
    141             while (!done) {
    142                 is = fis;
    143                 long size = 0;
    144                 int fileType = is.read();
    145                 ++countBytes;
    146                 if (fileType >= 0 && fileType < 0xf0) {
    147                     bytesToRead = 0;
    148                     size = readUnsignedNum64FromStream();
    149                     countBytes += size - bytesToRead; // bytesToRead is negative
    150                     bytesToRead = (int) size;
    151                    
    152                     switch(fileType) {
    153                     case NODE_DATASET:
    154                     case WAY_DATASET:
    155                     case REL_DATASET:
    156                     case BBOX_DATASET:
    157                     case TIMESTAMP_DATASET:
    158                     case HEADER_DATASET:
    159                         if (bytesToRead > ioBuf.length) {
    160                             ioBuf = new byte[bytesToRead+100];
    161                         }
    162                         int bytesRead = 0;
    163                         int neededBytes = bytesToRead;
    164                         while (neededBytes > 0) {
    165                             bytesRead += is.read(ioBuf, bytesRead, neededBytes);
    166                             neededBytes -= bytesRead;
    167                         }
    168                         ioPos = 0;
    169                         bis = new ByteArrayInputStream(ioBuf, 0, bytesToRead);
    170                         is = bis;
    171                         break;                   
    172                     default:   
     151            long size = 0;
     152            int fileType = is.read();
     153            ++countBytes;
     154            if (fileType >= 0 && fileType < 0xf0) {
     155                bytesToRead = 0;
     156                size = readUnsignedNum64FromStream();
     157                countBytes += size - bytesToRead; // bytesToRead is negative
     158                bytesToRead = (int) size;
     159
     160                switch(fileType) {
     161                case NODE_DATASET:
     162                case WAY_DATASET:
     163                case REL_DATASET:
     164                case BBOX_DATASET:
     165                case TIMESTAMP_DATASET:
     166                case HEADER_DATASET:
     167                    if (bytesToRead > ioBuf.length) {
     168                        ioBuf = new byte[bytesToRead+100];
    173169                    }
     170                    int bytesRead = 0;
     171                    int neededBytes = bytesToRead;
     172                    while (neededBytes > 0) {
     173                        bytesRead += is.read(ioBuf, bytesRead, neededBytes);
     174                        neededBytes -= bytesRead;
     175                    }
     176                    ioPos = 0;
     177                    bis = new ByteArrayInputStream(ioBuf, 0, bytesToRead);
     178                    is = bis;
     179                    break;                   
     180                default:   
    174181                }
    175                 if (fileType == EOF_FLAG) done = true;
    176                 else if (fileType == NODE_DATASET) readNode();
    177                 else if (fileType == WAY_DATASET) readWay();
    178                 else if (fileType == REL_DATASET) readRel();
    179                 else if (fileType == BBOX_DATASET) readBBox();
    180                 else if (fileType == TIMESTAMP_DATASET) readFileTimestamp();
    181                 else if (fileType == HEADER_DATASET) readHeader();
    182                 else if (fileType == EOD_FLAG) done = true;
    183                 else if (fileType == RESET_FLAG) reset();
    184                 else {
    185                     if (fileType < 0xf0) skip(size); // skip unknown data set
     182            }
     183            if (fileType == EOF_FLAG) done = true;
     184            else if (fileType == NODE_DATASET) readNode();
     185            else if (fileType == WAY_DATASET) readWay();
     186            else if (fileType == REL_DATASET) readRel();
     187            else if (fileType == BBOX_DATASET) readBBox();
     188            else if (fileType == TIMESTAMP_DATASET) readFileTimestamp();
     189            else if (fileType == HEADER_DATASET) readHeader();
     190            else if (fileType == EOD_FLAG) done = true;
     191            else if (fileType == RESET_FLAG) reset();
     192            else {
     193                if (fileType < 0xf0) skip(size); // skip unknown data set
     194            }
     195        }
     196    }
     197
     198    /**
     199     * read (and ignore) the file timestamp data set
     200     */
     201    private void readFileTimestamp() {
     202        /*long fileTimeStamp = */readSignedNum64();
     203    }
     204
     205    /**
     206     * Skip the given number of bytes
     207     * @param bytes number of bytes to skip
     208     * @throws IOException in case of I/O error
     209     */
     210    private void skip(long bytes) throws IOException {
     211        long toSkip = bytes;
     212        while (toSkip > 0) {
     213            toSkip -= is.skip(toSkip);
     214        }
     215    }
     216
     217    /**
     218     * read the bounding box data set
     219     */
     220    private void readBBox() {
     221        double minlon = FACTOR * 100L * readSignedNum32();
     222        double minlat = FACTOR * 100L * readSignedNum32();
     223        double maxlon = FACTOR * 100L * readSignedNum32();
     224        double maxlat = FACTOR * 100L * readSignedNum32();
     225
     226        Bounds b = new Bounds(minlat, minlon, maxlat, maxlon);
     227        if (!b.isCollapsed() && LatLon.isValidLat(minlat) && LatLon.isValidLat(maxlat)
     228                && LatLon.isValidLon(minlon) && LatLon.isValidLon(maxlon)) {
     229            ds.addDataSource(new DataSource(b, header));
     230        } else {
     231            Logging.error("Invalid Bounds: "+b);
     232        }
     233    }
     234
     235    /**
     236     * read a node data set
     237     * @throws IOException in case of I/O error
     238     */
     239    private void readNode() throws IOException {
     240        if (exception != null)
     241            return;
     242        try {
     243            lastNodeId += readSignedNum64();
     244            if (bytesToRead == 0)
     245                return; // only nodeId: this is a delete action, we ignore it
     246            readVersionTsAuthor();
     247
     248            if (bytesToRead == 0)
     249                return; // only nodeId+version: this is a delete action, we ignore it
     250            int lon = readSignedNum32() + lastLon; lastLon = lon;
     251            int lat = readSignedNum32() + lastLat; lastLat = lat;
     252
     253            double flon = FACTOR * (100L*lon);
     254            double flat = FACTOR * (100L*lat);
     255            assert flat >= -90.0 && flat <= 90.0; 
     256            assert flon >= -180.0 && flon <= 180.0; 
     257            if (version == 0)
     258                discourageUpload = true;
     259            Node node = new Node(lastNodeId, version == 0 ? 1 : version);
     260            node.setCoor(new LatLon(flat, flon).getRoundedToOsmPrecision());
     261
     262
     263            checkCoordinates(node.getCoor());
     264            checkChangesetId(lastChangeSet);
     265            node.setChangesetId((int) lastChangeSet);
     266            // User id
     267            if (lastTs != 0) {
     268                checkTimestamp(lastTs);
     269                node.setTimestamp(new Date(lastTs * 1000));
     270                if (osmUser != null)
     271                    node.setUser(osmUser);
     272            }
     273            if (bytesToRead > 0) {
     274                Map<String, String> keys = readTags();
     275                node.setKeys(keys);
     276            }
     277            externalIdMap.put(node.getPrimitiveId(), node);
     278        } catch (IllegalDataException e) {
     279            exception = e;
     280        }
     281
     282    }
     283
     284    /**
     285     * read a way data set
     286     * @throws IOException in case of I/O error
     287     */
     288    private void readWay() throws IOException {
     289        if (exception != null)
     290            return;
     291        try {
     292            lastWayId += readSignedNum64();
     293            if (bytesToRead == 0)
     294                return; // only wayId: this is a delete action, we ignore it
     295
     296            readVersionTsAuthor();
     297            if (bytesToRead == 0)
     298                return; // only wayId + version: this is a delete action, we ignore it
     299            if (version == 0)
     300                discourageUpload = true;
     301            final Way way = new Way(lastWayId, version == 0 ? 1 : version);
     302            checkChangesetId(lastChangeSet);
     303            way.setChangesetId((int) lastChangeSet);
     304            // User id
     305            if (lastTs != 0) {
     306                checkTimestamp(lastTs);
     307                way.setTimestamp(new Date(lastTs * 1000));
     308                if (osmUser != null)
     309                    way.setUser(osmUser);
     310            }
     311
     312            long refSize = readUnsignedNum32();
     313            long stop = bytesToRead - refSize;
     314            Collection<Long> nodeIds = new ArrayList<>();
     315
     316            while (bytesToRead > stop) {
     317                lastRef[0] += readSignedNum64();
     318                nodeIds.add(lastRef[0]);
     319            }
     320
     321            Map<String, String> keys = readTags();
     322            way.setKeys(keys);
     323            ways.put(way.getUniqueId(), nodeIds);
     324            externalIdMap.put(way.getPrimitiveId(), way);
     325        } catch (IllegalDataException e) {
     326            exception = e;
     327        }
     328
     329    }
     330
     331    /**
     332     * read a relation data set
     333     * @throws IOException in case of I/O error
     334     */
     335    private void readRel() throws IOException {
     336        if (exception != null)
     337            return;
     338        try {
     339            lastRelId += readSignedNum64();
     340            if (bytesToRead == 0)
     341                return; // only relId: this is a delete action, we ignore it
     342            readVersionTsAuthor();
     343            if (bytesToRead == 0)
     344                return; // only relId + version: this is a delete action, we ignore it
     345            if (version == 0)
     346                discourageUpload = true;
     347            final Relation rel = new Relation(lastRelId, version == 0 ? 1 : version);
     348            checkChangesetId(lastChangeSet);
     349            rel.setChangesetId((int) lastChangeSet);
     350            if (lastTs != 0) {
     351                checkTimestamp(lastTs);
     352                rel.setTimestamp(new Date(lastTs * 1000));
     353                if (osmUser != null)
     354                    rel.setUser(osmUser);
     355            }
     356
     357            long refSize = readUnsignedNum32();
     358            long stop = bytesToRead - refSize;
     359            Collection<RelationMemberData> members = new ArrayList<>();
     360            while (bytesToRead > stop) {
     361                long deltaRef = readSignedNum64();
     362                int refType = readRelRef();
     363                String role = stringPair[1];
     364                lastRef[refType] += deltaRef;
     365                long memId = lastRef[refType];
     366                OsmPrimitiveType type = null;
     367
     368                if (refType == 0) {
     369                    type = OsmPrimitiveType.NODE;
     370                } else if (refType == 1) {
     371                    type = OsmPrimitiveType.WAY;
     372                } else if (refType == 2) {
     373                    type = OsmPrimitiveType.RELATION;
    186374                }
    187             }
    188         }
    189        
    190         /**
    191          * read (and ignore) the file timestamp data set
    192          */
    193         private void readFileTimestamp() {
    194             /*long fileTimeStamp = */readSignedNum64();
    195         }
    196        
    197         /**
    198          * Skip the given number of bytes
    199          * @param bytes number of bytes to skip
    200          * @throws IOException in case of I/O error
    201          */
    202         private void skip(long bytes) throws IOException {
    203             long toSkip = bytes;
    204             while (toSkip > 0) {
    205                 toSkip -= is.skip(toSkip);
    206             }
    207         }
    208        
    209         /**
    210          * read the bounding box data set
    211          */
    212         private void readBBox() {
    213             double minlon = FACTOR * 100L * readSignedNum32();
    214             double minlat = FACTOR * 100L * readSignedNum32();
    215             double maxlon = FACTOR * 100L * readSignedNum32();
    216             double maxlat = FACTOR * 100L * readSignedNum32();
    217 
    218             Bounds b = new Bounds(minlat, minlon, maxlat, maxlon);
    219             if (!b.isCollapsed() && LatLon.isValidLat(minlat) && LatLon.isValidLat(maxlat)
    220                                  && LatLon.isValidLon(minlon) && LatLon.isValidLon(maxlon)) {
    221                 ds.addDataSource(new DataSource(b, header));
    222             } else {
    223                 Logging.error("Invalid Bounds: "+b);
    224             }
    225         }
    226 
    227         /**
    228          * read a node data set
    229          * @throws IOException in case of I/O error
    230          */
    231         private void readNode() throws IOException {
    232             if (exception != null)
    233                 return;
    234             try {
    235                 lastNodeId += readSignedNum64();
    236                 if (bytesToRead == 0)
    237                     return; // only nodeId: this is a delete action, we ignore it
    238                 readVersionTsAuthor();
    239 
    240                 if (bytesToRead == 0)
    241                     return; // only nodeId+version: this is a delete action, we ignore it
    242                 int lon = readSignedNum32() + lastLon; lastLon = lon;
    243                 int lat = readSignedNum32() + lastLat; lastLat = lat;
    244 
    245                 double flon = FACTOR * (100L*lon);
    246                 double flat = FACTOR * (100L*lat);
    247                 assert flat >= -90.0 && flat <= 90.0; 
    248                 assert flon >= -180.0 && flon <= 180.0; 
    249                 if (version == 0)
    250                     discourageUpload = true;
    251                 Node node = new Node(lastNodeId, version == 0 ? 1 : version);
    252                 node.setCoor(new LatLon(flat, flon).getRoundedToOsmPrecision());
    253                
    254 
    255                 checkCoordinates(node.getCoor());
    256                 checkChangesetId(lastChangeSet);
    257                 node.setChangesetId((int) lastChangeSet);
    258                 // User id
    259                 if (lastTs != 0) {
    260                     checkTimestamp(lastTs);
    261                     node.setTimestamp(new Date(lastTs * 1000));
    262                     if (osmUser != null)
    263                         node.setUser(osmUser);
     375                members.add(new RelationMemberData(role, type, memId));
     376            }
     377            Map<String, String> keys = readTags();
     378            rel.setKeys(keys);
     379            relations.put(rel.getUniqueId(), members);
     380            externalIdMap.put(rel.getPrimitiveId(), rel);
     381        } catch (IllegalDataException e) {
     382            exception = e;
     383        }
     384    }
     385
     386    private Map<String, String> readTags() throws IOException {
     387        Map<String, String> keys = new HashMap<>();
     388        while (bytesToRead > 0) {
     389            readStringPair();
     390            keys.put(stringPair[0], stringPair[1]);
     391        }
     392        assert bytesToRead == 0;
     393        return keys;
     394    }
     395
     396    /**
     397     * Store a new string pair (length check must be performed by caller)
     398     */
     399    private void storeStringPair() {
     400        stringTable[0][currStringTablePos] = stringPair[0];
     401        stringTable[1][currStringTablePos] = stringPair[1];
     402        ++currStringTablePos;
     403        if (currStringTablePos >= STRING_TABLE_SIZE)
     404            currStringTablePos = 0;
     405    }
     406
     407    /**
     408     * set stringPair to the values referenced by given string reference
     409     * No checking is performed.
     410     * @param ref valid values are 1 .. STRING_TABLE_SIZE
     411     */
     412    private void setStringRefPair(int ref) {
     413        int pos = currStringTablePos - ref;
     414        if (pos < 0)
     415            pos += STRING_TABLE_SIZE;
     416        stringPair[0] = stringTable[0][pos];
     417        stringPair[1] = stringTable[1][pos];
     418    }
     419
     420    /**
     421     * Read version, time stamp and change set and author. 
     422     * We are not interested in the values, but we have to maintain the string table.
     423     * @throws IOException in case of I/O error
     424     */
     425    private void readVersionTsAuthor() throws IOException {
     426        stringPair[0] = null;
     427        stringPair[1] = null;
     428        version = readUnsignedNum32();
     429        if (version != 0) {
     430            // version info
     431            long ts = readSignedNum64() + lastTs; lastTs = ts;
     432            if (ts != 0) {
     433                long changeSet = readSignedNum32() + lastChangeSet; lastChangeSet = changeSet;
     434                readAuthor();
     435            }
     436        }
     437    }
     438
     439    /**
     440     * Read author .
     441     * @throws IOException in case of I/O error
     442     */
     443    private void readAuthor() throws IOException {
     444        int stringRef = readUnsignedNum32();
     445        if (stringRef == 0) {
     446            long toReadStart = bytesToRead;
     447            long uidNum = readUnsignedNum64();
     448            if (uidNum == 0)
     449                stringPair[0] = "";
     450            else {
     451                stringPair[0] = Long.toString(uidNum);
     452                ioPos++; // skip terminating zero from uid
     453                --bytesToRead;
     454            }
     455            int start = 0;
     456            int buffPos = 0;
     457            stringPair[1] = null;
     458            while (stringPair[1] == null) {
     459                final int b = ioBuf[ioPos++];
     460                --bytesToRead;
     461                cnvBuffer[buffPos++] = (byte) b;
     462
     463                if (b == 0)
     464                    stringPair[1] = new String(cnvBuffer, start, buffPos-1, "UTF-8");
     465            }
     466            long bytes = toReadStart - bytesToRead;
     467            if (bytes <= MAX_STRING_PAIR_SIZE)
     468                storeStringPair();
     469        } else
     470            setStringRefPair(stringRef);
     471        if (stringPair[0] != null && stringPair[0].isEmpty() == false) {
     472            long uid = Long.parseLong(stringPair[0]);
     473            osmUser = User.createOsmUser(uid, stringPair[1]);
     474        } else
     475            osmUser = null;
     476    }
     477
     478    /**
     479     * read object type ("0".."2") concatenated with role (single string)
     480     * @return 0..3 for type (3 means unknown)
     481     * @throws IOException in case of I/O error
     482     */
     483    private int readRelRef() throws IOException {
     484        int refType = -1;
     485        long toReadStart = bytesToRead;
     486        int stringRef = readUnsignedNum32();
     487        if (stringRef == 0) {
     488            refType = ioBuf[ioPos++] - 0x30;
     489            --bytesToRead;
     490
     491            if (refType < 0 || refType > 2)
     492                refType = 3;
     493            stringPair[0] = REL_REF_TYPES[refType];
     494
     495            int start = 0;
     496            int buffPos = 0;
     497            stringPair[1] = null;
     498            while (stringPair[1] == null) {
     499                final int b = ioBuf[ioPos++];
     500                --bytesToRead;
     501                cnvBuffer[buffPos++] = (byte) b;
     502
     503                if (b == 0)
     504                    stringPair[1] = new String(cnvBuffer, start, buffPos-1, "UTF-8");
     505            }
     506            long bytes = toReadStart - bytesToRead;
     507            if (bytes <= MAX_STRING_PAIR_SIZE)
     508                storeStringPair();
     509        } else {
     510            setStringRefPair(stringRef);
     511            char c = stringPair[0].charAt(0);
     512            switch (c) {
     513            case 'n': refType = 0; break;
     514            case 'w': refType = 1; break;
     515            case 'r': refType = 2; break;
     516            default: refType = 3;
     517            }
     518        }
     519        return refType;
     520    }
     521
     522    /**
     523     * read a string pair (see o5m definition)
     524     * @throws IOException in case of I/O error
     525     */
     526    private void readStringPair() throws IOException {
     527        int stringRef = readUnsignedNum32();
     528        if (stringRef == 0) {
     529            long toReadStart = bytesToRead;
     530            int cnt = 0;
     531            int buffPos = 0;
     532            int start = 0;
     533            while (cnt < 2) {
     534                final int b = ioBuf[ioPos++];
     535                --bytesToRead;
     536                cnvBuffer[buffPos++] = (byte) b;
     537
     538                if (b == 0) {
     539                    stringPair[cnt] = new String(cnvBuffer, start, buffPos-start-1, "UTF-8");
     540                    ++cnt;
     541                    start = buffPos;
    264542                }
    265                 if (bytesToRead > 0) {
    266                     Map<String, String> keys = readTags();
    267                     node.setKeys(keys);
    268                 }
    269                 externalIdMap.put(node.getPrimitiveId(), node);
    270             } catch (IllegalDataException e) {
    271                 exception = e;
    272             }
    273 
    274         }
    275        
    276         /**
    277          * read a way data set
    278          * @throws IOException in case of I/O error
    279          */
    280         private void readWay() throws IOException {
    281             if (exception != null)
    282                 return;
    283             try {
    284                 lastWayId += readSignedNum64();
    285                 if (bytesToRead == 0)
    286                     return; // only wayId: this is a delete action, we ignore it
    287 
    288                 readVersionTsAuthor();
    289                 if (bytesToRead == 0)
    290                     return; // only wayId + version: this is a delete action, we ignore it
    291                 if (version == 0)
    292                     discourageUpload = true;
    293                 final Way way = new Way(lastWayId, version == 0 ? 1 : version);
    294                 checkChangesetId(lastChangeSet);
    295                 way.setChangesetId((int) lastChangeSet);
    296                 // User id
    297                 if (lastTs != 0) {
    298                     checkTimestamp(lastTs);
    299                     way.setTimestamp(new Date(lastTs * 1000));
    300                     if (osmUser != null)
    301                         way.setUser(osmUser);
    302                 }
    303 
    304                 long refSize = readUnsignedNum32();
    305                 long stop = bytesToRead - refSize;
    306                 Collection<Long> nodeIds = new ArrayList<>();
    307 
    308                 while (bytesToRead > stop) {
    309                     lastRef[0] += readSignedNum64();
    310                     nodeIds.add(lastRef[0]);
    311                 }
    312 
    313                 Map<String, String> keys = readTags();
    314                 way.setKeys(keys);
    315                 ways.put(way.getUniqueId(), nodeIds);
    316                 externalIdMap.put(way.getPrimitiveId(), way);
    317             } catch (IllegalDataException e) {
    318                 exception = e;
    319             }
    320 
    321         }
    322 
    323         /**
    324          * read a relation data set
    325          * @throws IOException in case of I/O error
    326          */
    327         private void readRel() throws IOException {
    328             if (exception != null)
    329                 return;
    330             try {
    331                 lastRelId += readSignedNum64();
    332                 if (bytesToRead == 0)
    333                     return; // only relId: this is a delete action, we ignore it
    334                 readVersionTsAuthor();
    335                 if (bytesToRead == 0)
    336                     return; // only relId + version: this is a delete action, we ignore it
    337                 if (version == 0)
    338                     discourageUpload = true;
    339                 final Relation rel = new Relation(lastRelId, version == 0 ? 1 : version);
    340                 checkChangesetId(lastChangeSet);
    341                 rel.setChangesetId((int) lastChangeSet);
    342                 if (lastTs != 0) {
    343                     checkTimestamp(lastTs);
    344                     rel.setTimestamp(new Date(lastTs * 1000));
    345                     if (osmUser != null)
    346                         rel.setUser(osmUser);
    347                 }
    348 
    349                 long refSize = readUnsignedNum32();
    350                 long stop = bytesToRead - refSize;
    351                 Collection<RelationMemberData> members = new ArrayList<>();
    352                 while (bytesToRead > stop) {
    353                     long deltaRef = readSignedNum64();
    354                     int refType = readRelRef();
    355                     String role = stringPair[1];
    356                     lastRef[refType] += deltaRef;
    357                     long memId = lastRef[refType];
    358                     OsmPrimitiveType type = null;
    359 
    360                     if (refType == 0) {
    361                         type = OsmPrimitiveType.NODE;
    362                     } else if (refType == 1) {
    363                         type = OsmPrimitiveType.WAY;
    364                     } else if (refType == 2) {
    365                         type = OsmPrimitiveType.RELATION;
    366                     }
    367                     members.add(new RelationMemberData(role, type, memId));
    368                 }
    369                 Map<String, String> keys = readTags();
    370                 rel.setKeys(keys);
    371                 relations.put(rel.getUniqueId(), members);
    372                 externalIdMap.put(rel.getPrimitiveId(), rel);
    373             } catch (IllegalDataException e) {
    374                 exception = e;
    375             }
    376         }
    377        
    378         private Map<String, String> readTags() throws IOException {
    379             Map<String, String> keys = new HashMap<>();
    380             while (bytesToRead > 0) {
    381                 readStringPair();
    382                 keys.put(stringPair[0], stringPair[1]);
    383             }
    384             assert bytesToRead == 0;
    385             return keys;
    386         }
    387        
    388         /**
    389          * Store a new string pair (length check must be performed by caller)
    390          */
    391         private void storeStringPair() {
    392             stringTable[0][currStringTablePos] = stringPair[0];
    393             stringTable[1][currStringTablePos] = stringPair[1];
    394             ++currStringTablePos;
    395             if (currStringTablePos >= STRING_TABLE_SIZE)
    396                 currStringTablePos = 0;
    397         }
    398 
    399         /**
    400          * set stringPair to the values referenced by given string reference
    401          * No checking is performed.
    402          * @param ref valid values are 1 .. STRING_TABLE_SIZE
    403          */
    404         private void setStringRefPair(int ref) {
    405             int pos = currStringTablePos - ref;
    406             if (pos < 0)
    407                 pos += STRING_TABLE_SIZE;
    408             stringPair[0] = stringTable[0][pos];
    409             stringPair[1] = stringTable[1][pos];
    410         }
    411 
    412         /**
    413          * Read version, time stamp and change set and author. 
    414          * We are not interested in the values, but we have to maintain the string table.
    415          * @throws IOException in case of I/O error
    416          */
    417         private void readVersionTsAuthor() throws IOException {
    418             stringPair[0] = null;
    419             stringPair[1] = null;
    420             version = readUnsignedNum32();
    421             if (version != 0) {
    422                 // version info
    423                 long ts = readSignedNum64() + lastTs; lastTs = ts;
    424                 if (ts != 0) {
    425                     long changeSet = readSignedNum32() + lastChangeSet; lastChangeSet = changeSet;
    426                     readAuthor();
    427                 }
    428             }
    429         }
    430        
    431         /**
    432          * Read author .
    433          * @throws IOException in case of I/O error
    434          */
    435         private void readAuthor() throws IOException {
    436             int stringRef = readUnsignedNum32();
    437             if (stringRef == 0) {
    438                 long toReadStart = bytesToRead;
    439                 long uidNum = readUnsignedNum64();
    440                 if (uidNum == 0)
    441                     stringPair[0] = "";
    442                 else {
    443                     stringPair[0] = Long.toString(uidNum);
    444                     ioPos++; // skip terminating zero from uid
    445                     --bytesToRead;
    446                 }
    447                 int start = 0;
    448                 int buffPos = 0;
    449                 stringPair[1] = null;
    450                 while (stringPair[1] == null) {
    451                     final int b = ioBuf[ioPos++];
    452                     --bytesToRead;
    453                     cnvBuffer[buffPos++] = (byte) b;
    454 
    455                     if (b == 0)
    456                         stringPair[1] = new String(cnvBuffer, start, buffPos-1, "UTF-8");
    457                 }
    458                 long bytes = toReadStart - bytesToRead;
    459                 if (bytes <= MAX_STRING_PAIR_SIZE)
    460                     storeStringPair();
    461             } else
    462                 setStringRefPair(stringRef);
    463             if (stringPair[0] != null && stringPair[0].isEmpty() == false) {
    464                 long uid = Long.parseLong(stringPair[0]);
    465                 osmUser = User.createOsmUser(uid, stringPair[1]);
    466             } else
    467                 osmUser = null;
    468         }
    469        
    470         /**
    471          * read object type ("0".."2") concatenated with role (single string)
    472          * @return 0..3 for type (3 means unknown)
    473          * @throws IOException in case of I/O error
    474          */
    475         private int readRelRef() throws IOException {
    476             int refType = -1;
    477             long toReadStart = bytesToRead;
    478             int stringRef = readUnsignedNum32();
    479             if (stringRef == 0) {
    480                 refType = ioBuf[ioPos++] - 0x30;
    481                 --bytesToRead;
    482 
    483                 if (refType < 0 || refType > 2)
    484                     refType = 3;
    485                 stringPair[0] = REL_REF_TYPES[refType];
    486                    
    487                 int start = 0;
    488                 int buffPos = 0;
    489                 stringPair[1] = null;
    490                 while (stringPair[1] == null) {
    491                     final int b = ioBuf[ioPos++];
    492                     --bytesToRead;
    493                     cnvBuffer[buffPos++] = (byte) b;
    494 
    495                     if (b == 0)
    496                         stringPair[1] = new String(cnvBuffer, start, buffPos-1, "UTF-8");
    497                 }
    498                 long bytes = toReadStart - bytesToRead;
    499                 if (bytes <= MAX_STRING_PAIR_SIZE)
    500                     storeStringPair();
    501             } else {
    502                 setStringRefPair(stringRef);
    503                 char c = stringPair[0].charAt(0);
    504                 switch (c) {
    505                 case 'n': refType = 0; break;
    506                 case 'w': refType = 1; break;
    507                 case 'r': refType = 2; break;
    508                 default: refType = 3;
    509                 }
    510             }
    511             return refType;
    512         }
    513        
    514         /**
    515          * read a string pair (see o5m definition)
    516          * @throws IOException in case of I/O error
    517          */
    518         private void readStringPair() throws IOException {
    519             int stringRef = readUnsignedNum32();
    520             if (stringRef == 0) {
    521                 long toReadStart = bytesToRead;
    522                 int cnt = 0;
    523                 int buffPos = 0;
    524                 int start = 0;
    525                 while (cnt < 2) {
    526                     final int b = ioBuf[ioPos++];
    527                     --bytesToRead;
    528                     cnvBuffer[buffPos++] = (byte) b;
    529 
    530                     if (b == 0) {
    531                         stringPair[cnt] = new String(cnvBuffer, start, buffPos-start-1, "UTF-8");
    532                         ++cnt;
    533                         start = buffPos;
    534                     }
    535                 }
    536                 long bytes = toReadStart - bytesToRead;
    537                 if (bytes <= MAX_STRING_PAIR_SIZE)
    538                     storeStringPair();
    539             } else
    540                 setStringRefPair(stringRef);
    541         }
    542        
    543         /** reset the delta values and string table */
    544         private void reset() {
    545             lastNodeId = 0; lastWayId = 0; lastRelId = 0;
    546             lastRef[0] = 0; lastRef[1] = 0; lastRef[2] = 0;
    547             lastTs = 0; lastChangeSet = 0;
    548             lastLon = 0; lastLat = 0;
    549             stringTable = new String[2][STRING_TABLE_SIZE];
    550             currStringTablePos = 0;
    551         }
    552 
    553         /**
    554          * read and verify o5m header (known values are o5m2 and o5c2)
    555          * @throws IOException in case of I/O error
    556          */
    557         private void readHeader() throws IOException {
    558             if (ioBuf[0] != 'o' || ioBuf[1] != '5' || (ioBuf[2] != 'c' && ioBuf[2] != 'm') || ioBuf[3] != '2') {
    559                 throw new IOException(tr("unsupported header"));
    560             }
    561             header = new String(ioBuf, 0, 3, "UTF-8");
    562         }
    563        
    564         /**
    565          * read a varying length signed number (see o5m definition)
    566          * @return the number
    567          */
    568         private int readSignedNum32() {
    569             int result;
    570             int b = ioBuf[ioPos++];
     543            }
     544            long bytes = toReadStart - bytesToRead;
     545            if (bytes <= MAX_STRING_PAIR_SIZE)
     546                storeStringPair();
     547        } else
     548            setStringRefPair(stringRef);
     549    }
     550
     551    /** reset the delta values and string table */
     552    private void reset() {
     553        lastNodeId = 0; lastWayId = 0; lastRelId = 0;
     554        lastRef[0] = 0; lastRef[1] = 0; lastRef[2] = 0;
     555        lastTs = 0; lastChangeSet = 0;
     556        lastLon = 0; lastLat = 0;
     557        stringTable = new String[2][STRING_TABLE_SIZE];
     558        currStringTablePos = 0;
     559    }
     560
     561    /**
     562     * read and verify o5m header (known values are o5m2 and o5c2)
     563     * @throws IOException in case of I/O error
     564     */
     565    private void readHeader() throws IOException {
     566        if (ioBuf[0] != 'o' || ioBuf[1] != '5' || (ioBuf[2] != 'c' && ioBuf[2] != 'm') || ioBuf[3] != '2') {
     567            throw new IOException(tr("unsupported header"));
     568        }
     569        header = new String(ioBuf, 0, 3, "UTF-8");
     570    }
     571
     572    /**
     573     * read a varying length signed number (see o5m definition)
     574     * @return the number as int
     575     */
     576    private int readSignedNum32() {
     577        int result;
     578        int b = ioBuf[ioPos++];
     579        --bytesToRead;
     580        result = b;
     581        if ((b & 0x80) == 0) {  // just one byte
     582            if ((b & 0x01) == 1)
     583                return -1-(result >> 1);
     584            return result >> 1;
     585        }
     586        int sign = b & 0x01;
     587        result = (result & 0x7e) >> 1;
     588        int shift = 6;
     589        while (((b = ioBuf[ioPos++]) & 0x80) != 0) { // more bytes will follow
    571590            --bytesToRead;
    572             result = b;
    573             if ((b & 0x80) == 0) {  // just one byte
    574                 if ((b & 0x01) == 1)
    575                     return -1-(result >> 1);
    576                 return result >> 1;
    577             }
    578             int sign = b & 0x01;
    579             result = (result & 0x7e) >> 1;
    580             int fac = 0x40;
    581             while (((b = ioBuf[ioPos++]) & 0x80) != 0) { // more bytes will follow
    582                 --bytesToRead;
    583                 result += fac * (b & 0x7f);
    584                 fac <<= 7;
    585             }
     591            result += (b & 0x7f) << shift;
     592            shift += 7;
     593        }
     594        --bytesToRead;
     595        result += b << shift;
     596        if (sign == 1) // negative
     597            return -1 - result;
     598        return result;
     599
     600    }
     601
     602    /**
     603     * read a varying length signed number (see o5m definition)
     604     * @return the number as long
     605     */
     606    private long readSignedNum64() {
     607        long result;
     608        int b = ioBuf[ioPos++];
     609        --bytesToRead;
     610        result = b;
     611        if ((b & 0x80) == 0) {  // just one byte
     612            if ((b & 0x01) == 1)
     613                return -1 - (result >> 1);
     614            return result >> 1;
     615        }
     616        int sign = b & 0x01;
     617        result = (result & 0x7e) >> 1;
     618        int shift = 6;
     619        while (((b = ioBuf[ioPos++]) & 0x80) != 0) { // more bytes will follow
    586620            --bytesToRead;
    587             result += fac * b;
    588             if (sign == 1) // negative
    589                 return -1-result;
     621            result += ((long) (b & 0x7f)) << shift;
     622            shift += 7;
     623        }
     624        --bytesToRead;
     625        result += ((long) b) << shift;
     626        if (sign == 1) // negative
     627            return -1 - result;
     628        return result;
     629    }
     630
     631    /**
     632     * read a varying length unsigned number (see o5m definition)
     633     * @return the number as long
     634     * @throws IOException in case of I/O error
     635     */
     636    private long readUnsignedNum64FromStream()throws IOException {
     637        int b = is.read();
     638        --bytesToRead;
     639        long result = b;
     640        if ((b & 0x80) == 0) {  // just one byte
    590641            return result;
    591 
    592         }
    593 
    594         /**
    595          * read a varying length signed number (see o5m definition)
    596          * @return the number
    597          */
    598         private long readSignedNum64() {
    599             long result;
    600             int b = ioBuf[ioPos++];
     642        }
     643        result &= 0x7f;
     644        int shift = 7;
     645        while (((b = is.read()) & 0x80) != 0) { // more bytes will follow
    601646            --bytesToRead;
    602             result = b;
    603             if ((b & 0x80) == 0) {  // just one byte
    604                 if ((b & 0x01) == 1)
    605                     return -1-(result >> 1);
    606                 return result >> 1;
    607             }
    608             int sign = b & 0x01;
    609             result = (result & 0x7e) >> 1;
    610             long fac = 0x40;
    611             while (((b = ioBuf[ioPos++]) & 0x80) != 0) { // more bytes will follow
    612                 --bytesToRead;
    613                 result += fac * (b & 0x7f);
    614                 fac <<= 7;
    615             }
     647            result += ((long) (b & 0x7f)) << shift;
     648            shift += 7;
     649        }
     650        --bytesToRead;
     651        result += ((long) b) << shift;
     652        return result;
     653    }
     654
     655
     656    /**
     657     * read a varying length unsigned number (see o5m definition)
     658     * @return the number as long
     659     */
     660    private long readUnsignedNum64() {
     661        int b = ioBuf[ioPos++];
     662        --bytesToRead;
     663        long result = b;
     664        if ((b & 0x80) == 0) {  // just one byte
     665            return result;
     666        }
     667        result &= 0x7f;
     668        int shift = 7;
     669        while (((b = ioBuf[ioPos++]) & 0x80) != 0) { // more bytes will follow
    616670            --bytesToRead;
    617             result += fac * b;
    618             if (sign == 1) // negative
    619                 return -1-result;
     671            result += ((long) (b & 0x7f)) << shift;
     672            shift += 7;
     673        }
     674        --bytesToRead;
     675        result += ((long) b) << shift;
     676        return result;
     677    }
     678
     679    /**
     680     * read a varying length unsigned number (see o5m definition)
     681     * is similar to the 64 bit version.
     682     * @return the number as int
     683     */
     684    private int readUnsignedNum32() {
     685        int b = ioBuf[ioPos++];
     686        --bytesToRead;
     687        int result = b;
     688        if ((b & 0x80) == 0) {  // just one byte
    620689            return result;
    621 
    622         }
    623 
    624         /**
    625          * read a varying length unsigned number (see o5m definition)
    626          * @return a long
    627          * @throws IOException in case of I/O error
    628          */
    629         private long readUnsignedNum64FromStream()throws IOException {
    630             int b = is.read();
     690        }
     691        result &= 0x7f;
     692        int shift = 7;
     693        while (((b = ioBuf[ioPos++]) & 0x80) != 0) { // more bytes will follow
    631694            --bytesToRead;
    632             long result = b;
    633             if ((b & 0x80) == 0) {  // just one byte
    634                 return result;
    635             }
    636             result &= 0x7f;
    637             long fac = 0x80;
    638             while (((b = is.read()) & 0x80) != 0) { // more bytes will follow
    639                 --bytesToRead;
    640                 result += fac * (b & 0x7f);
    641                 fac <<= 7;
    642             }
    643             --bytesToRead;
    644             result += fac * b;
    645             return result;
    646         }
    647        
    648        
    649         /**
    650          * read a varying length unsigned number (see o5m definition)
    651          * @return a long
    652          */
    653         private long readUnsignedNum64() {
    654             int b = ioBuf[ioPos++];
    655             --bytesToRead;
    656             long result = b;
    657             if ((b & 0x80) == 0) {  // just one byte
    658                 return result;
    659             }
    660             result &= 0x7f;
    661             long fac = 0x80;
    662             while (((b = ioBuf[ioPos++]) & 0x80) != 0) { // more bytes will follow
    663                 --bytesToRead;
    664                 result += fac * (b & 0x7f);
    665                 fac <<= 7;
    666             }
    667             --bytesToRead;
    668             result += fac * b;
    669             return result;
    670         }
    671 
    672         /**
    673          * read a varying length unsigned number (see o5m definition)
    674          * is similar to the 64 bit version.
    675          * @return an int
    676          */
    677         private int readUnsignedNum32() {
    678             int b = ioBuf[ioPos++];
    679             --bytesToRead;
    680             int result = b;
    681             if ((b & 0x80) == 0) {  // just one byte
    682                 return result;
    683             }
    684             result &= 0x7f;
    685             long fac = 0x80;
    686             while (((b = ioBuf[ioPos++]) & 0x80) != 0) { // more bytes will follow
    687                 --bytesToRead;
    688                 result += fac * (b & 0x7f);
    689                 fac <<= 7;
    690             }
    691             --bytesToRead;
    692             result += fac * b;
    693             return result;
    694         }
    695 
    696        
     695            result += (b & 0x7f) << shift;
     696            shift += 7;
     697        }
     698        --bytesToRead;
     699        result += b << shift;
     700        return result;
     701    }
     702
     703    /**
     704     * Exception thrown after user cancellation.
     705     */
     706    private static final class O5mParsingCancelException extends Exception implements ImportCancelException {
     707        O5mParsingCancelException(String msg) {
     708            super(msg);
     709        }
     710    }
    697711   
    698712    /**
     
    711725        }
    712726        CheckParameterUtil.ensureParameterNotNull(source, "source");
    713         return new O5mReader(source).doParseDataSet(source, progressMonitor);
     727        return new O5mReader().doParseDataSet(source, progressMonitor);
    714728    }
    715729
     
    717731    protected DataSet doParseDataSet(InputStream source, ProgressMonitor progressMonitor)
    718732            throws IllegalDataException {
     733        ProgressMonitor.CancelListener cancelListener = () -> cancel = true;
     734        progressMonitor.addCancelListener(cancelListener);
    719735        try {
    720             progressMonitor.beginTask(tr("Prepare OSM data...", 2));
     736            progressMonitor.beginTask(tr("Prepare OSM data..."), 3); // read, prepare, render
    721737            progressMonitor.indeterminateSubTask(tr("Reading OSM data..."));
    722738
    723             parse();
     739            parse(source);
    724740            progressMonitor.worked(1);
    725 
     741           
    726742            progressMonitor.indeterminateSubTask(tr("Preparing data set..."));
    727743            prepareDataSet();
    728744            progressMonitor.worked(1);
     745            if (cancel) {
     746                throw new O5mParsingCancelException(tr("Import was canceled while preparing data"));
     747            }
     748            progressMonitor.indeterminateSubTask(tr("Rendering data set..."));
    729749            return getDataSet();
    730750        } catch (IllegalDataException e) {
     
    733753            throw new IllegalDataException(e);
    734754        } finally {
    735             progressMonitor.finishTask();
     755            progressMonitor.removeCancelListener(cancelListener);
    736756        }
    737757    }
Note: See TracChangeset for help on using the changeset viewer.