Index: src/org/openstreetmap/josm/actions/ExtensionFileFilter.java
===================================================================
--- src/org/openstreetmap/josm/actions/ExtensionFileFilter.java	(revision 4459)
+++ src/org/openstreetmap/josm/actions/ExtensionFileFilter.java	(working copy)
@@ -40,6 +40,7 @@
         String[] importerNames = {
                 "org.openstreetmap.josm.io.OsmImporter",
                 "org.openstreetmap.josm.io.OsmGzipImporter",
+                "org.openstreetmap.josm.io.PbfImporter",
                 "org.openstreetmap.josm.io.GpxImporter",
                 "org.openstreetmap.josm.io.NMEAImporter",
                 "org.openstreetmap.josm.io.OsmBzip2Importer",
Index: src/org/openstreetmap/josm/data/coor/LatLon.java
===================================================================
--- src/org/openstreetmap/josm/data/coor/LatLon.java	(revision 4459)
+++ src/org/openstreetmap/josm/data/coor/LatLon.java	(working copy)
@@ -63,6 +63,15 @@
     public static boolean isValidLon(double lon) {
         return lon >= -180d && lon <= 180d;
     }
+    
+    /**
+     * Replies true if lat is in the range [-90,90] and lon is in the range [-180,180]
+     *
+     * @return true if lat is in the range [-90,90] and lon is in the range [-180,180]
+     */
+    public boolean isValid() {
+        return isValidLat(lat()) && isValidLon(lon());
+    }
 
     /**
      * Replies the coordinate in degrees/minutes/seconds format
Index: src/org/openstreetmap/josm/data/osm/AbstractPrimitive.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/AbstractPrimitive.java	(revision 4459)
+++ src/org/openstreetmap/josm/data/osm/AbstractPrimitive.java	(working copy)
@@ -86,7 +86,7 @@
      * 0 if it wasn't uploaded to a changeset yet of if the changeset
      * id isn't known.
      */
-    protected int changesetId;
+    protected long changesetId;
 
     protected int timestamp;
 
@@ -233,7 +233,7 @@
      * @return the id of the changeset this primitive was last uploaded to.
      */
     @Override
-    public int getChangesetId() {
+    public long getChangesetId() {
         return changesetId;
     }
 
@@ -246,7 +246,7 @@
      * @throws IllegalArgumentException thrown if id < 0
      */
     @Override
-    public void setChangesetId(int changesetId) throws IllegalStateException, IllegalArgumentException {
+    public void setChangesetId(long changesetId) throws IllegalStateException, IllegalArgumentException {
         if (this.changesetId == changesetId)
             return;
         if (changesetId < 0)
@@ -254,7 +254,7 @@
         if (isNew() && changesetId > 0)
             throw new IllegalStateException(tr("Cannot assign a changesetId > 0 to a new primitive. Value of changesetId is {0}", changesetId));
 
-        int old = this.changesetId;
+        long old = this.changesetId;
         this.changesetId = changesetId;
     }
 
Index: src/org/openstreetmap/josm/data/osm/Changeset.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/Changeset.java	(revision 4459)
+++ src/org/openstreetmap/josm/data/osm/Changeset.java	(working copy)
@@ -17,7 +17,7 @@
  */
 public final class Changeset implements Tagged {
     /** the changeset id */
-    private int id;
+    private long id;
     /** the user who owns the changeset */
     private User user;
     /** date this changeset was created at */
@@ -52,7 +52,7 @@
      *
      * @param id the id
      */
-    public Changeset(int id) {
+    public Changeset(long id) {
         this.id = id;
         this.incomplete = id > 0;
         this.tags = new HashMap<String, String>();
@@ -83,7 +83,7 @@
     }
 
     public int compareTo(Changeset other) {
-        return Integer.valueOf(getId()).compareTo(other.getId());
+        return Long.valueOf(getId()).compareTo(other.getId());
     }
 
     public String getName() {
@@ -95,11 +95,11 @@
         return formatter.format(this);
     }
 
-    public int getId() {
+    public long getId() {
         return id;
     }
 
-    public void setId(int id) {
+    public void setId(long id) {
         this.id = id;
     }
 
@@ -228,13 +228,20 @@
             return false;
         return true;
     }
-
+    
+    /* (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
     @Override
     public int hashCode() {
-        if (id > 0)
-            return id;
-        else
+        if (id > 0) {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + (int) (id ^ (id >>> 32));
+            return result;
+        } else {
             return super.hashCode();
+        }
     }
 
     @Override
Index: src/org/openstreetmap/josm/data/osm/ChangesetCache.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/ChangesetCache.java	(revision 4459)
+++ src/org/openstreetmap/josm/data/osm/ChangesetCache.java	(working copy)
@@ -46,7 +46,7 @@
     }
 
     /** the cached changesets */
-    private final Map<Integer, Changeset> cache  = new HashMap<Integer, Changeset>();
+    private final Map<Long, Changeset> cache  = new HashMap<Long, Changeset>();
 
     private final CopyOnWriteArrayList<ChangesetCacheListener> listeners =
         new CopyOnWriteArrayList<ChangesetCacheListener>();
@@ -106,7 +106,7 @@
         fireChangesetCacheEvent(e);
     }
 
-    public boolean contains(int id) {
+    public boolean contains(long id) {
         if (id <=0) return false;
         return cache.get(id) != null;
     }
@@ -117,7 +117,7 @@
         return contains(cs.getId());
     }
 
-    public Changeset get(int id) {
+    public Changeset get(long id) {
         return cache.get(id);
     }
 
@@ -125,7 +125,7 @@
         return new HashSet<Changeset>(cache.values());
     }
 
-    protected void remove(int id, DefaultChangesetCacheEvent e) {
+    protected void remove(long id, DefaultChangesetCacheEvent e) {
         if (id <= 0) return;
         Changeset cs = cache.get(id);
         if (cs == null) return;
@@ -133,7 +133,7 @@
         e.rememberRemovedChangeset(cs);
     }
 
-    public void remove(int id) {
+    public void remove(long id) {
         DefaultChangesetCacheEvent e = new DefaultChangesetCacheEvent(this);
         remove(id, e);
         if (! e.isEmpty()) {
Index: src/org/openstreetmap/josm/data/osm/DataSet.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/DataSet.java	(revision 4459)
+++ src/org/openstreetmap/josm/data/osm/DataSet.java	(working copy)
@@ -1058,7 +1058,7 @@
         fireEvent(new WayNodesChangedEvent(this, way));
     }
 
-    void fireChangesetIdChanged(OsmPrimitive primitive, int oldChangesetId, int newChangesetId) {
+    void fireChangesetIdChanged(OsmPrimitive primitive, long oldChangesetId, long newChangesetId) {
         fireEvent(new ChangesetIdChangedEvent(this, Collections.singletonList(primitive), oldChangesetId, newChangesetId));
     }
 
Index: src/org/openstreetmap/josm/data/osm/IPrimitive.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/IPrimitive.java	(revision 4459)
+++ src/org/openstreetmap/josm/data/osm/IPrimitive.java	(working copy)
@@ -27,8 +27,8 @@
     Date getTimestamp();
     void setTimestamp(Date timestamp);
     boolean isTimestampEmpty();
-    int getChangesetId();
-    void setChangesetId(int changesetId);
+    long getChangesetId();
+    void setChangesetId(long changesetId);
 
     void visit(PrimitiveVisitor visitor);
     String getName();
Index: src/org/openstreetmap/josm/data/osm/OsmPrimitive.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/OsmPrimitive.java	(revision 4459)
+++ src/org/openstreetmap/josm/data/osm/OsmPrimitive.java	(working copy)
@@ -384,10 +384,10 @@
     }
 
     @Override
-    public void setChangesetId(int changesetId) throws IllegalStateException, IllegalArgumentException {
+    public void setChangesetId(long changesetId) throws IllegalStateException, IllegalArgumentException {
         boolean locked = writeLock();
         try {
-            int old = this.changesetId;
+            long old = this.changesetId;
             super.setChangesetId(changesetId);
             if (dataSet != null) {
                 dataSet.fireChangesetIdChanged(this, old, changesetId);
Index: src/org/openstreetmap/josm/data/osm/event/ChangesetIdChangedEvent.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/event/ChangesetIdChangedEvent.java	(revision 4459)
+++ src/org/openstreetmap/josm/data/osm/event/ChangesetIdChangedEvent.java	(working copy)
@@ -9,10 +9,10 @@
 public class ChangesetIdChangedEvent extends AbstractDatasetChangedEvent {
 
     private final List<OsmPrimitive> primitives;
-    private final int oldChangesetId;
-    private final int newChangesetId;
+    private final long oldChangesetId;
+    private final long newChangesetId;
 
-    public ChangesetIdChangedEvent(DataSet dataSet, List<OsmPrimitive> primitives, int oldChangesetId, int newChangesetId) {
+    public ChangesetIdChangedEvent(DataSet dataSet, List<OsmPrimitive> primitives, long oldChangesetId, long newChangesetId) {
         super(dataSet);
         this.primitives = primitives;
         this.oldChangesetId = oldChangesetId;
@@ -34,11 +34,11 @@
         return DatasetEventType.CHANGESET_ID_CHANGED;
     }
 
-    public int getOldChangesetId() {
+    public long getOldChangesetId() {
         return oldChangesetId;
     }
 
-    public int getNewChangesetId() {
+    public long getNewChangesetId() {
         return newChangesetId;
     }
 
Index: src/org/openstreetmap/josm/gui/dialogs/ChangesetDialog.java
===================================================================
--- src/org/openstreetmap/josm/gui/dialogs/ChangesetDialog.java	(revision 4459)
+++ src/org/openstreetmap/josm/gui/dialogs/ChangesetDialog.java	(working copy)
@@ -256,7 +256,7 @@
         public void mouseClicked(MouseEvent e) {
             if (!SwingUtilities.isLeftMouseButton(e) || e.getClickCount() < 2)
                 return;
-            Set<Integer> sel = getCurrentChangesetListModel().getSelectedChangesetIds();
+            Set<Long> sel = getCurrentChangesetListModel().getSelectedChangesetIds();
             if (sel.isEmpty())
                 return;
             if (Main.main.getCurrentDataSet() == null)
@@ -292,7 +292,7 @@
             updateEnabledState();
         }
 
-        public void selectObjectsByChangesetIds(DataSet ds, Set<Integer> ids) {
+        public void selectObjectsByChangesetIds(DataSet ds, Set<Long> ids) {
             if (ds == null || ids == null)
                 return;
             Set<OsmPrimitive> sel = new HashSet<OsmPrimitive>();
@@ -308,7 +308,7 @@
             if (Main.main.getEditLayer() == null)
                 return;
             ChangesetListModel model = getCurrentChangesetListModel();
-            Set<Integer> sel = model.getSelectedChangesetIds();
+            Set<Long> sel = model.getSelectedChangesetIds();
             if (sel.isEmpty())
                 return;
 
@@ -344,7 +344,7 @@
 
         public void actionPerformed(ActionEvent arg0) {
             ChangesetListModel model = getCurrentChangesetListModel();
-            Set<Integer> sel = model.getSelectedChangesetIds();
+            Set<Long> sel = model.getSelectedChangesetIds();
             if (sel.isEmpty())
                 return;
             ChangesetHeaderDownloadTask task = new ChangesetHeaderDownloadTask(sel);
@@ -448,7 +448,7 @@
             putValue(SMALL_ICON, ImageProvider.get("dialogs/changeset", "changesetmanager"));
         }
 
-        protected void launchChangesetManager(Collection<Integer> toSelect) {
+        protected void launchChangesetManager(Collection<Long> toSelect) {
             ChangesetCacheManager cm = ChangesetCacheManager.getInstance();
             if (cm.isVisible()) {
                 cm.setExtendedState(Frame.NORMAL);
@@ -464,10 +464,10 @@
 
         public void actionPerformed(ActionEvent arg0) {
             ChangesetListModel model = getCurrentChangesetListModel();
-            Set<Integer> sel = model.getSelectedChangesetIds();
-            final Set<Integer> toDownload = new HashSet<Integer>();
+            Set<Long> sel = model.getSelectedChangesetIds();
+            final Set<Long> toDownload = new HashSet<Long>();
             ChangesetCache cc = ChangesetCache.getInstance();
-            for (int id: sel) {
+            for (long id: sel) {
                 if (!cc.contains(id)) {
                     toDownload.add(id);
                 }
Index: src/org/openstreetmap/josm/gui/dialogs/InspectPrimitiveDialog.java
===================================================================
--- src/org/openstreetmap/josm/gui/dialogs/InspectPrimitiveDialog.java	(revision 4459)
+++ src/org/openstreetmap/josm/gui/dialogs/InspectPrimitiveDialog.java	(working copy)
@@ -221,7 +221,7 @@
             add(tr("Edited by: "), o.getUser() == null ? tr("<new object>")
                     : getNameAndId(o.getUser().getName(), o.getUser().getId()));
             add(tr("Version: "), Integer.toString(o.getVersion()));
-            add(tr("In changeset: "), Integer.toString(o.getChangesetId()));
+            add(tr("In changeset: "), Long.toString(o.getChangesetId()));
         }
 
         void addAttributes(OsmPrimitive o) {
Index: src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetCacheManager.java
===================================================================
--- src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetCacheManager.java	(revision 4459)
+++ src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetCacheManager.java	(working copy)
@@ -606,14 +606,14 @@
      *
      * @param ids the collection of ids. If null, the selection is cleared.
      */
-    public void setSelectedChangesetsById(Collection<Integer> ids) {
+    public void setSelectedChangesetsById(Collection<Long> ids) {
         if (ids == null) {
             setSelectedChangesets(null);
             return;
         }
         Set<Changeset> toSelect = new HashSet<Changeset>();
         ChangesetCache cc = ChangesetCache.getInstance();
-        for (int id: ids) {
+        for (long id: ids) {
             if (cc.contains(id)) {
                 toSelect.add(cc.get(id));
             }
Index: src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetCacheManagerModel.java
===================================================================
--- src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetCacheManagerModel.java	(revision 4459)
+++ src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetCacheManagerModel.java	(working copy)
@@ -89,8 +89,8 @@
      *
      * @return a set of ids of the selected changesets
      */
-    public Set<Integer> getSelectedChangesetIds() {
-        Set<Integer> ret = new HashSet<Integer>();
+    public Set<Long> getSelectedChangesetIds() {
+        Set<Long> ret = new HashSet<Long>();
         for (Changeset cs: getSelectedChangesets()) {
             ret.add(cs.getId());
         }
Index: src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetCacheTableCellRenderer.java
===================================================================
--- src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetCacheTableCellRenderer.java	(revision 4459)
+++ src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetCacheTableCellRenderer.java	(working copy)
@@ -41,7 +41,7 @@
     }
 
     protected void renderId(Changeset cs) {
-        setText(Integer.toString(cs.getId()));
+        setText(Long.toString(cs.getId()));
         setToolTipText("");
     }
 
Index: src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetContentDownloadTask.java
===================================================================
--- src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetContentDownloadTask.java	(revision 4459)
+++ src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetContentDownloadTask.java	(working copy)
@@ -31,7 +31,7 @@
 public class ChangesetContentDownloadTask extends PleaseWaitRunnable implements ChangesetDownloadTask{
 
     /** the list of changeset ids to download */
-    private final List<Integer> toDownload = new ArrayList<Integer>();
+    private final List<Long> toDownload = new ArrayList<Long>();
     /** true if the task was canceled */
     private boolean canceled;
     /** keeps the last exception thrown in the task, if any */
@@ -46,11 +46,11 @@
      *
      * @param ids the collection of ids. May be null.
      */
-    protected void init(Collection<Integer> ids) {
+    protected void init(Collection<Long> ids) {
         if (ids == null) {
             ids = Collections.emptyList();
         }
-        for (Integer id: ids) {
+        for (Long id: ids) {
             if (id == null || id <= 0) {
                 continue;
             }
@@ -65,7 +65,7 @@
      * @param changesetId the changeset id. >0 required.
      * @throws IllegalArgumentException thrown if changesetId <= 0
      */
-    public ChangesetContentDownloadTask(int changesetId) throws IllegalArgumentException{
+    public ChangesetContentDownloadTask(long changesetId) throws IllegalArgumentException{
         super(tr("Downloading changeset content"), false /* don't ignore exceptions */);
         if (changesetId <= 0)
             throw new IllegalArgumentException(MessageFormat.format("Expected integer value > 0 for parameter ''{0}'', got ''{1}''", "changesetId", changesetId));
@@ -78,7 +78,7 @@
      *
      * @param changesetIds the changeset ids. Empty collection assumed, if null.
      */
-    public ChangesetContentDownloadTask(Collection<Integer> changesetIds) {
+    public ChangesetContentDownloadTask(Collection<Long> changesetIds) {
         super(tr("Downloading changeset content"), false /* don't ignore exceptions */);
         init(changesetIds);
     }
@@ -91,7 +91,7 @@
      * @throws IllegalArgumentException thrown if changesetId <= 0
      * @throws IllegalArgumentException thrown if parent is null
      */
-    public ChangesetContentDownloadTask(Component parent, int changesetId) throws IllegalArgumentException{
+    public ChangesetContentDownloadTask(Component parent, long changesetId) throws IllegalArgumentException{
         super(parent, tr("Downloading changeset content"), false /* don't ignore exceptions */);
         if (changesetId <= 0)
             throw new IllegalArgumentException(MessageFormat.format("Expected integer value > 0 for parameter ''{0}'', got ''{1}''", "changesetId", changesetId));
@@ -106,7 +106,7 @@
      * @param changesetIds the changeset ids. Empty collection assumed, if null.
      * @throws IllegalArgumentException thrown if parent is null
      */
-    public ChangesetContentDownloadTask(Component parent, Collection<Integer> changesetIds) throws IllegalArgumentException {
+    public ChangesetContentDownloadTask(Component parent, Collection<Long> changesetIds) throws IllegalArgumentException {
         super(parent, tr("Downloading changeset content"), false /* don't ignore exceptions */);
         init(changesetIds);
     }
@@ -119,7 +119,7 @@
      * @return true if the local {@see ChangesetCache} already includes the changeset with
      * id <code>changesetId</code>
      */
-    protected boolean isAvailableLocally(int changesetId) {
+    protected boolean isAvailableLocally(long changesetId) {
         return ChangesetCache.getInstance().get(changesetId) != null;
     }
 
@@ -130,7 +130,7 @@
      * @param changesetId the changeset id
      * @throws OsmTransferException thrown if something went wrong
      */
-    protected void downloadChangeset(int changesetId) throws OsmTransferException {
+    protected void downloadChangeset(long changesetId) throws OsmTransferException {
         synchronized(this) {
             reader = new OsmServerChangesetReader();
         }
@@ -164,7 +164,7 @@
         try {
             getProgressMonitor().setTicksCount(toDownload.size());
             int i=0;
-            for (int id: toDownload) {
+            for (long id: toDownload) {
                 i++;
                 if (!isAvailableLocally(id)) {
                     getProgressMonitor().setCustomText(tr("({0}/{1}) Downloading changeset {2}...", i, toDownload.size(), id));
Index: src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetDetailPanel.java
===================================================================
--- src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetDetailPanel.java	(revision 4459)
+++ src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetDetailPanel.java	(working copy)
@@ -206,7 +206,7 @@
     protected void updateView(Changeset cs) {
         String msg;
         if (cs == null) return;
-        tfID.setText(Integer.toString(cs.getId()));
+        tfID.setText(Long.toString(cs.getId()));
         String comment = cs.get("comment");
         taComment.setText(comment == null ? "" : comment);
 
Index: src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetHeaderDownloadTask.java
===================================================================
--- src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetHeaderDownloadTask.java	(revision 4459)
+++ src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetHeaderDownloadTask.java	(working copy)
@@ -64,7 +64,7 @@
             changesets = Collections.emptyList();
         }
 
-        HashSet<Integer> ids = new HashSet<Integer>();
+        HashSet<Long> ids = new HashSet<Long>();
         for (Changeset cs: changesets) {
             if (cs == null || cs.isNew()) {
                 continue;
@@ -78,20 +78,20 @@
 
     }
 
-    private Set<Integer> idsToDownload;
+    private Set<Long> idsToDownload;
     private OsmServerChangesetReader reader;
     private boolean canceled;
     private Exception lastException;
     private Set<Changeset> downloadedChangesets;
 
-    protected void init(Collection<Integer> ids) {
+    protected void init(Collection<Long> ids) {
         if (ids == null) {
             ids = Collections.emptyList();
         }
-        idsToDownload = new HashSet<Integer>();
+        idsToDownload = new HashSet<Long>();
         if (ids == null ||  ids.isEmpty())
             return;
-        for (int id: ids) {
+        for (long id: ids) {
             if (id <= 0) {
                 continue;
             }
@@ -107,7 +107,7 @@
      *
      * @param ids the collection of ids. Empty collection assumed if null.
      */
-    public ChangesetHeaderDownloadTask(Collection<Integer> ids) {
+    public ChangesetHeaderDownloadTask(Collection<Long> ids) {
         // parent for dialog is Main.parent
         super(tr("Download changesets"), false /* don't ignore exceptions */);
         init(ids);
@@ -123,7 +123,7 @@
      * @param ids the collection of ids. Empty collection assumed if null.
      * @throws IllegalArgumentException thrown if dialogParent is null
      */
-    public ChangesetHeaderDownloadTask(Component dialogParent, Collection<Integer> ids) throws IllegalArgumentException{
+    public ChangesetHeaderDownloadTask(Component dialogParent, Collection<Long> ids) throws IllegalArgumentException{
         super(dialogParent,tr("Download changesets"), false /* don't ignore exceptions */);
         init(ids);
     }
Index: src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetListModel.java
===================================================================
--- src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetListModel.java	(revision 4459)
+++ src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetListModel.java	(working copy)
@@ -39,8 +39,8 @@
         return ret;
     }
 
-    public Set<Integer> getSelectedChangesetIds() {
-        Set<Integer> ret = new HashSet<Integer>();
+    public Set<Long> getSelectedChangesetIds() {
+        Set<Long> ret = new HashSet<Long>();
         for (int i=0; i < getSize(); i++) {
             if (selectionModel.isSelectedIndex(i)) {
                 ret.add(data.get(i).getId());
Index: src/org/openstreetmap/josm/gui/dialogs/changeset/SingleChangesetDownloadPanel.java
===================================================================
--- src/org/openstreetmap/josm/gui/dialogs/changeset/SingleChangesetDownloadPanel.java	(revision 4459)
+++ src/org/openstreetmap/josm/gui/dialogs/changeset/SingleChangesetDownloadPanel.java	(working copy)
@@ -83,7 +83,7 @@
         public void actionPerformed(ActionEvent arg0) {
             if (!isEnabled())
                 return;
-            int id = getChangesetId();
+            long id = getChangesetId();
             if (id == 0) return;
             ChangesetContentDownloadTask task =  new ChangesetContentDownloadTask(
                     SingleChangesetDownloadPanel.this,
Index: src/org/openstreetmap/josm/io/AbstractReader.java
===================================================================
--- src/org/openstreetmap/josm/io/AbstractReader.java	(revision 0)
+++ src/org/openstreetmap/josm/io/AbstractReader.java	(revision 0)
@@ -0,0 +1,213 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.io;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.openstreetmap.josm.data.osm.Changeset;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
+import org.openstreetmap.josm.data.osm.PrimitiveId;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.RelationMember;
+import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
+import org.openstreetmap.josm.data.osm.Way;
+
+/**
+ * Base class for OsmReader (XML file format) and PbfReader (Protobuf file format)
+ * @author Vincent
+ *
+ */
+public abstract class AbstractReader {
+    
+    /**
+     * Used as a temporary storage for relation members, before they
+     * are resolved into pointers to real objects.
+     */
+    protected static class RelationMemberData {
+        public OsmPrimitiveType type;
+        public long id;
+        public String role;
+    }
+
+    /**
+     * The dataset to add parsed objects to.
+     */
+    protected final DataSet ds = new DataSet();
+
+    protected Changeset uploadChangeset;
+
+    /** the map from external ids to read OsmPrimitives. External ids are
+     * longs too, but in contrast to internal ids negative values are used
+     * to identify primitives unknown to the OSM server
+     */
+    protected Map<PrimitiveId, OsmPrimitive> externalIdMap = new HashMap<PrimitiveId, OsmPrimitive>();
+
+    /**
+     * Data structure for the remaining way objects
+     */
+    protected Map<Long, Collection<Long>> ways = new HashMap<Long, Collection<Long>>();
+
+    /**
+     * Data structure for relation objects
+     */
+    protected Map<Long, Collection<RelationMemberData>> relations = new HashMap<Long, Collection<RelationMemberData>>();
+
+    /**
+     * Replies the parsed data set
+     *
+     * @return the parsed data set
+     */
+    public DataSet getDataSet() {
+        return ds;
+    }
+    
+    /**
+     * Processes the parsed nodes after parsing. Just adds them to
+     * the dataset
+     *
+     */
+    private void processNodesAfterParsing() {
+        for (OsmPrimitive primitive: externalIdMap.values()) {
+            if (primitive instanceof Node) {
+                this.ds.addPrimitive(primitive);
+            }
+        }
+    }
+
+    /**
+     * Processes the ways after parsing. Rebuilds the list of nodes of each way and
+     * adds the way to the dataset
+     *
+     * @throws IllegalDataException thrown if a data integrity problem is detected
+     */
+    private void processWaysAfterParsing() throws IllegalDataException{
+        for (Long externalWayId: ways.keySet()) {
+            Way w = (Way)externalIdMap.get(new SimplePrimitiveId(externalWayId, OsmPrimitiveType.WAY));
+            List<Node> wayNodes = new ArrayList<Node>();
+            for (long id : ways.get(externalWayId)) {
+                Node n = (Node)externalIdMap.get(new SimplePrimitiveId(id, OsmPrimitiveType.NODE));
+                if (n == null) {
+                    if (id <= 0)
+                        throw new IllegalDataException (
+                                tr("Way with external ID ''{0}'' includes missing node with external ID ''{1}''.",
+                                        externalWayId,
+                                        id));
+                    // create an incomplete node if necessary
+                    //
+                    n = (Node)ds.getPrimitiveById(id,OsmPrimitiveType.NODE);
+                    if (n == null) {
+                        n = new Node(id);
+                        ds.addPrimitive(n);
+                    }
+                }
+                if (n.isDeleted()) {
+                    System.out.println(tr("Deleted node {0} is part of way {1}", id, w.getId()));
+                } else {
+                    wayNodes.add(n);
+                }
+            }
+            w.setNodes(wayNodes);
+            if (w.hasIncompleteNodes()) {
+                  System.out.println(tr("Way {0} with {1} nodes has incomplete nodes because at least one node was missing in the loaded data.",
+                          externalWayId, w.getNodesCount()));
+            }
+            ds.addPrimitive(w);
+        }
+    }
+
+    /**
+     * Completes the parsed relations with its members.
+     *
+     * @throws IllegalDataException thrown if a data integrity problem is detected, i.e. if a
+     * relation member refers to a local primitive which wasn't available in the data
+     *
+     */
+    private void processRelationsAfterParsing() throws IllegalDataException {
+
+        // First add all relations to make sure that when relation reference other relation, the referenced will be already in dataset
+        for (Long externalRelationId : relations.keySet()) {
+            Relation relation = (Relation) externalIdMap.get(
+                    new SimplePrimitiveId(externalRelationId, OsmPrimitiveType.RELATION)
+            );
+            ds.addPrimitive(relation);
+        }
+
+        for (Long externalRelationId : relations.keySet()) {
+            Relation relation = (Relation) externalIdMap.get(
+                    new SimplePrimitiveId(externalRelationId, OsmPrimitiveType.RELATION)
+            );
+            List<RelationMember> relationMembers = new ArrayList<RelationMember>();
+            for (RelationMemberData rm : relations.get(externalRelationId)) {
+                OsmPrimitive primitive = null;
+
+                // lookup the member from the map of already created primitives
+                primitive = externalIdMap.get(new SimplePrimitiveId(rm.id, rm.type));
+
+                if (primitive == null) {
+                    if (rm.id <= 0)
+                        // relation member refers to a primitive with a negative id which was not
+                        // found in the data. This is always a data integrity problem and we abort
+                        // with an exception
+                        //
+                        throw new IllegalDataException(
+                                tr("Relation with external id ''{0}'' refers to a missing primitive with external id ''{1}''.",
+                                        externalRelationId,
+                                        rm.id));
+
+                    // member refers to OSM primitive which was not present in the parsed data
+                    // -> create a new incomplete primitive and add it to the dataset
+                    //
+                    primitive = ds.getPrimitiveById(rm.id, rm.type);
+                    if (primitive == null) {
+                        switch (rm.type) {
+                        case NODE:
+                            primitive = new Node(rm.id); break;
+                        case WAY:
+                            primitive = new Way(rm.id); break;
+                        case RELATION:
+                            primitive = new Relation(rm.id); break;
+                        default: throw new AssertionError(); // can't happen
+                        }
+
+                        ds.addPrimitive(primitive);
+                        externalIdMap.put(new SimplePrimitiveId(rm.id, rm.type), primitive);
+                    }
+                }
+                if (primitive.isDeleted()) {
+                    System.out.println(tr("Deleted member {0} is used by relation {1}", primitive.getId(), relation.getId()));
+                } else {
+                    relationMembers.add(new RelationMember(rm.role, primitive));
+                }
+            }
+            relation.setMembers(relationMembers);
+        }
+    }
+
+    private void processChangesetAfterParsing() {
+        if (uploadChangeset != null) {
+            for (Map.Entry<String, String> e : uploadChangeset.getKeys().entrySet()) {
+                ds.addChangeSetTag(e.getKey(), e.getValue());
+            }
+        }
+    }
+    
+    protected final void prepareDataSet() throws IllegalDataException {
+        try {
+            ds.beginUpdate();
+            processNodesAfterParsing();
+            processWaysAfterParsing();
+            processRelationsAfterParsing();
+            processChangesetAfterParsing();
+        } finally {
+            ds.endUpdate();
+        }
+    }
+}
Index: src/org/openstreetmap/josm/io/AllFormatsImporter.java
===================================================================
--- src/org/openstreetmap/josm/io/AllFormatsImporter.java	(revision 4459)
+++ src/org/openstreetmap/josm/io/AllFormatsImporter.java	(working copy)
@@ -12,7 +12,7 @@
  */
 public class AllFormatsImporter extends FileImporter {
     public AllFormatsImporter() {
-        super(new ExtensionFileFilter("osm,xml,osm.gz,osm.bz2,osm.bz,gpx,gpx.gz,nmea,nme,nma,log,txt,wms,jpg", "", tr("All Formats")
+        super(new ExtensionFileFilter("osm,xml,osm.gz,osm.bz2,osm.bz,osm.pbf,gpx,gpx.gz,nmea,nme,nma,log,txt,wms,jpg", "", tr("All Formats")
                     + " (*.gpx *.osm *.nmea *.jpg ...)"));
     }
     @Override public boolean acceptFile(File pathname) {
Index: src/org/openstreetmap/josm/io/OsmImporter.java
===================================================================
--- src/org/openstreetmap/josm/io/OsmImporter.java	(revision 4459)
+++ src/org/openstreetmap/josm/io/OsmImporter.java	(working copy)
@@ -42,6 +42,10 @@
     protected void importData(InputStream in, final File associatedFile) throws IllegalDataException {
         final DataSet dataSet = OsmReader.parseDataSet(in, NullProgressMonitor.INSTANCE);
         final OsmDataLayer layer = new OsmDataLayer(dataSet, associatedFile.getName(), associatedFile);
+        addDataLayer(dataSet, layer, associatedFile.getPath());
+    }
+    
+    protected void addDataLayer(final DataSet dataSet, final OsmDataLayer layer, final String filePath) {
         // FIXME: remove UI stuff from IO subsystem
         //
         Runnable uiStuff = new Runnable() {
@@ -50,7 +54,7 @@
                 if (dataSet.allPrimitives().isEmpty()) {
                     JOptionPane.showMessageDialog(
                             Main.parent,
-                            tr("No data found in file {0}.", associatedFile.getPath()),
+                            tr("No data found in file {0}.", filePath),
                             tr("Open OSM file"),
                             JOptionPane.INFORMATION_MESSAGE);
                 }
Index: src/org/openstreetmap/josm/io/OsmReader.java
===================================================================
--- src/org/openstreetmap/josm/io/OsmReader.java	(revision 4459)
+++ src/org/openstreetmap/josm/io/OsmReader.java	(working copy)
@@ -8,18 +8,14 @@
 import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import javax.xml.stream.Location;
 import javax.xml.stream.XMLInputFactory;
-import javax.xml.stream.XMLStreamReader;
 import javax.xml.stream.XMLStreamConstants;
 import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.Location;
+import javax.xml.stream.XMLStreamReader;
 
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.coor.LatLon;
@@ -28,14 +24,10 @@
 import org.openstreetmap.josm.data.osm.DataSource;
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.NodeData;
-import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
 import org.openstreetmap.josm.data.osm.PrimitiveData;
-import org.openstreetmap.josm.data.osm.PrimitiveId;
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.RelationData;
-import org.openstreetmap.josm.data.osm.RelationMember;
-import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
 import org.openstreetmap.josm.data.osm.Tagged;
 import org.openstreetmap.josm.data.osm.User;
 import org.openstreetmap.josm.data.osm.Way;
@@ -52,65 +44,14 @@
  * The XMLStreamReader cursor points to the start of the element, when the method is
  * entered, and it must point to the end of the same element, when it is exited.
  */
-public class OsmReader {
-
-    /**
-     * Used as a temporary storage for relation members, before they
-     * are resolved into pointers to real objects.
-     */
-    private static class RelationMemberData {
-        public OsmPrimitiveType type;
-        public long id;
-        public String role;
-    }
-
-    /**
-     * The dataset to add parsed objects to.
-     */
-    private DataSet ds = new DataSet();
+public class OsmReader extends AbstractReader {
 
     private XMLStreamReader parser;
 
-    /** the map from external ids to read OsmPrimitives. External ids are
-     * longs too, but in contrast to internal ids negative values are used
-     * to identify primitives unknown to the OSM server
-     */
-    private Map<PrimitiveId, OsmPrimitive> externalIdMap = new HashMap<PrimitiveId, OsmPrimitive>();
-
-    /**
-     * Data structure for the remaining way objects
-     */
-    private Map<Long, Collection<Long>> ways = new HashMap<Long, Collection<Long>>();
-
-    /**
-     * Data structure for relation objects
-     */
-    private Map<Long, Collection<RelationMemberData>> relations = new HashMap<Long, Collection<RelationMemberData>>();
-
-    private Changeset uploadChangeset;
-
-    /**
-     * constructor (for private use only)
-     *
-     * @see #parseDataSet(InputStream, DataSet, ProgressMonitor)
-     */
-    private OsmReader() {
-        externalIdMap = new HashMap<PrimitiveId, OsmPrimitive>();
-    }
-
     public void setParser(XMLStreamReader parser) {
         this.parser = parser;
     }
 
-    /**
-     * Replies the parsed data set
-     *
-     * @return the parsed data set
-     */
-    public DataSet getDataSet() {
-        return ds;
-    }
-
     protected void throwException(String msg) throws XMLStreamException {
         throw new OsmParsingException(msg, parser.getLocation());
     }
@@ -136,6 +77,7 @@
         parser.close();
     }
 
+    @SuppressWarnings("null")
     private void parseOsm() throws XMLStreamException {
         String v = parser.getAttributeValue(null, "version");
         if (v == null) {
@@ -351,6 +293,7 @@
         }
     }
 
+    @SuppressWarnings("null")
     private void parseTag(Tagged t) throws XMLStreamException {
         String key = parser.getAttributeValue(null, "k");
         String value = parser.getAttributeValue(null, "v");
@@ -579,136 +522,6 @@
     }
 
     /**
-     * Processes the parsed nodes after parsing. Just adds them to
-     * the dataset
-     *
-     */
-    protected void processNodesAfterParsing() {
-        for (OsmPrimitive primitive: externalIdMap.values()) {
-            if (primitive instanceof Node) {
-                this.ds.addPrimitive(primitive);
-            }
-        }
-    }
-
-    /**
-     * Processes the ways after parsing. Rebuilds the list of nodes of each way and
-     * adds the way to the dataset
-     *
-     * @throws IllegalDataException thrown if a data integrity problem is detected
-     */
-    protected void processWaysAfterParsing() throws IllegalDataException{
-        for (Long externalWayId: ways.keySet()) {
-            Way w = (Way)externalIdMap.get(new SimplePrimitiveId(externalWayId, OsmPrimitiveType.WAY));
-            List<Node> wayNodes = new ArrayList<Node>();
-            for (long id : ways.get(externalWayId)) {
-                Node n = (Node)externalIdMap.get(new SimplePrimitiveId(id, OsmPrimitiveType.NODE));
-                if (n == null) {
-                    if (id <= 0)
-                        throw new IllegalDataException (
-                                tr("Way with external ID ''{0}'' includes missing node with external ID ''{1}''.",
-                                        externalWayId,
-                                        id));
-                    // create an incomplete node if necessary
-                    //
-                    n = (Node)ds.getPrimitiveById(id,OsmPrimitiveType.NODE);
-                    if (n == null) {
-                        n = new Node(id);
-                        ds.addPrimitive(n);
-                    }
-                }
-                if (n.isDeleted()) {
-                    System.out.println(tr("Deleted node {0} is part of way {1}", id, w.getId()));
-                } else {
-                    wayNodes.add(n);
-                }
-            }
-            w.setNodes(wayNodes);
-            if (w.hasIncompleteNodes()) {
-                  System.out.println(tr("Way {0} with {1} nodes has incomplete nodes because at least one node was missing in the loaded data.",
-                          externalWayId, w.getNodesCount()));
-            }
-            ds.addPrimitive(w);
-        }
-    }
-
-    /**
-     * Completes the parsed relations with its members.
-     *
-     * @throws IllegalDataException thrown if a data integrity problem is detected, i.e. if a
-     * relation member refers to a local primitive which wasn't available in the data
-     *
-     */
-    private void processRelationsAfterParsing() throws IllegalDataException {
-
-        // First add all relations to make sure that when relation reference other relation, the referenced will be already in dataset
-        for (Long externalRelationId : relations.keySet()) {
-            Relation relation = (Relation) externalIdMap.get(
-                    new SimplePrimitiveId(externalRelationId, OsmPrimitiveType.RELATION)
-            );
-            ds.addPrimitive(relation);
-        }
-
-        for (Long externalRelationId : relations.keySet()) {
-            Relation relation = (Relation) externalIdMap.get(
-                    new SimplePrimitiveId(externalRelationId, OsmPrimitiveType.RELATION)
-            );
-            List<RelationMember> relationMembers = new ArrayList<RelationMember>();
-            for (RelationMemberData rm : relations.get(externalRelationId)) {
-                OsmPrimitive primitive = null;
-
-                // lookup the member from the map of already created primitives
-                primitive = externalIdMap.get(new SimplePrimitiveId(rm.id, rm.type));
-
-                if (primitive == null) {
-                    if (rm.id <= 0)
-                        // relation member refers to a primitive with a negative id which was not
-                        // found in the data. This is always a data integrity problem and we abort
-                        // with an exception
-                        //
-                        throw new IllegalDataException(
-                                tr("Relation with external id ''{0}'' refers to a missing primitive with external id ''{1}''.",
-                                        externalRelationId,
-                                        rm.id));
-
-                    // member refers to OSM primitive which was not present in the parsed data
-                    // -> create a new incomplete primitive and add it to the dataset
-                    //
-                    primitive = ds.getPrimitiveById(rm.id, rm.type);
-                    if (primitive == null) {
-                        switch (rm.type) {
-                        case NODE:
-                            primitive = new Node(rm.id); break;
-                        case WAY:
-                            primitive = new Way(rm.id); break;
-                        case RELATION:
-                            primitive = new Relation(rm.id); break;
-                        default: throw new AssertionError(); // can't happen
-                        }
-
-                        ds.addPrimitive(primitive);
-                        externalIdMap.put(new SimplePrimitiveId(rm.id, rm.type), primitive);
-                    }
-                }
-                if (primitive.isDeleted()) {
-                    System.out.println(tr("Deleted member {0} is used by relation {1}", primitive.getId(), relation.getId()));
-                } else {
-                    relationMembers.add(new RelationMember(rm.role, primitive));
-                }
-            }
-            relation.setMembers(relationMembers);
-        }
-    }
-
-    private void processChangesetAfterParsing() {
-        if (uploadChangeset != null) {
-            for (Map.Entry<String, String> e : uploadChangeset.getKeys().entrySet()) {
-                ds.addChangeSetTag(e.getKey(), e.getValue());
-            }
-        }
-    }
-
-    /**
      * Parse the given input source and return the dataset.
      *
      * @param source the source input stream. Must not be null.
@@ -735,15 +548,7 @@
             progressMonitor.worked(1);
 
             progressMonitor.indeterminateSubTask(tr("Preparing data set..."));
-            reader.ds.beginUpdate();
-            try {
-                reader.processNodesAfterParsing();
-                reader.processWaysAfterParsing();
-                reader.processRelationsAfterParsing();
-                reader.processChangesetAfterParsing();
-            } finally {
-                reader.ds.endUpdate();
-            }
+            reader.prepareDataSet();
             progressMonitor.worked(1);
             return reader.getDataSet();
         } catch(IllegalDataException e) {
Index: src/org/openstreetmap/josm/io/OsmServerChangesetReader.java
===================================================================
--- src/org/openstreetmap/josm/io/OsmServerChangesetReader.java	(revision 4459)
+++ src/org/openstreetmap/josm/io/OsmServerChangesetReader.java	(working copy)
@@ -120,7 +120,7 @@
      * @throws OsmTransferException thrown if something goes wrong
      * @throws IllegalArgumentException if id <= 0
      */
-    public List<Changeset> readChangesets(Collection<Integer> ids, ProgressMonitor monitor) throws OsmTransferException {
+    public List<Changeset> readChangesets(Collection<Long> ids, ProgressMonitor monitor) throws OsmTransferException {
         if (ids == null)
             return Collections.emptyList();
         if (monitor == null) {
@@ -131,8 +131,8 @@
             monitor.setTicksCount(ids.size());
             List<Changeset> ret = new ArrayList<Changeset>();
             int i=0;
-            for (Iterator<Integer> it = ids.iterator(); it.hasNext(); ) {
-                int id = it.next();
+            for (Iterator<Long> it = ids.iterator(); it.hasNext(); ) {
+                long id = it.next();
                 if (id <= 0) {
                     continue;
                 }
@@ -169,7 +169,7 @@
      * @throws IllegalArgumentException thrown if id <= 0
      * @throws OsmTransferException thrown if something went wrong
      */
-    public ChangesetDataSet downloadChangeset(int id, ProgressMonitor monitor) throws IllegalArgumentException, OsmTransferException {
+    public ChangesetDataSet downloadChangeset(long id, ProgressMonitor monitor) throws IllegalArgumentException, OsmTransferException {
         if (id <= 0)
             throw new IllegalArgumentException(MessageFormat.format("Expected value of type integer > 0 for parameter ''{0}'', got {1}", "id", id));
         if (monitor == null) {
Index: src/org/openstreetmap/josm/io/PbfImporter.java
===================================================================
--- src/org/openstreetmap/josm/io/PbfImporter.java	(revision 0)
+++ src/org/openstreetmap/josm/io/PbfImporter.java	(revision 0)
@@ -0,0 +1,33 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.io;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.File;
+import java.io.InputStream;
+
+import org.openstreetmap.josm.actions.ExtensionFileFilter;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
+
+/**
+ * @author Vincent
+ *
+ */
+public class PbfImporter extends OsmImporter {
+    
+    public PbfImporter() {
+        super(new ExtensionFileFilter("osm.pbf", "osm.pbf", tr("OSM Server Files PBF compressed") + " (*.osm.pbf)"));
+    }
+
+    /* (non-Javadoc)
+     * @see org.openstreetmap.josm.io.OsmImporter#importData(java.io.InputStream, java.io.File)
+     */
+    @Override
+    protected void importData(InputStream in, File associatedFile) throws IllegalDataException {
+        final DataSet dataSet = PbfReader.parseDataSet(in, NullProgressMonitor.INSTANCE);
+        final OsmDataLayer layer = new OsmDataLayer(dataSet, associatedFile.getName(), associatedFile);
+        addDataLayer(dataSet, layer, associatedFile.getPath());
+    }
+}
Index: src/org/openstreetmap/josm/io/PbfReader.java
===================================================================
--- src/org/openstreetmap/josm/io/PbfReader.java	(revision 0)
+++ src/org/openstreetmap/josm/io/PbfReader.java	(revision 0)
@@ -0,0 +1,207 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.io;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.User;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.tools.CheckParameterUtil;
+
+import crosby.binary.BinaryParser;
+import crosby.binary.Osmformat;
+import crosby.binary.Osmformat.DenseNodes;
+import crosby.binary.Osmformat.HeaderBlock;
+import crosby.binary.file.BlockInputStream;
+
+/**
+ * @author Vincent
+ *
+ */
+public class PbfReader extends AbstractReader {
+    
+    protected class PbfParser extends BinaryParser {
+
+        @Override
+        protected void parse(HeaderBlock header) {
+        }
+
+        @Override
+        protected void parseDense(DenseNodes nodes) {
+            int keyindex = 0;
+            // Almost all data is DELTA coded
+            long previousId = 0;
+            long previousLat = 0;
+            long previousLon = 0;
+            long previousChangesetId = 0;
+            int previousUid = 0;
+            int previousSuid = 0;
+            long previousTimestamp = 0;
+            for (int i = 0; i < nodes.getIdCount(); i++) {
+                // Id (delta) and version (normal)
+                Node node = new Node(previousId+=nodes.getId(i), nodes.getDenseinfo().getVersion(i));
+                // Lat/Lon (delta)
+                node.setCoor(new LatLon(parseLat(previousLat+=nodes.getLat(i)), parseLon(previousLon+=nodes.getLon(i))));
+                // Changeset (delta)
+                node.setChangesetId(previousChangesetId+=nodes.getDenseinfo().getChangeset(i));
+                // User (delta)
+                node.setUser(User.createOsmUser(previousUid+=nodes.getDenseinfo().getUid(i), getStringById(previousSuid+=nodes.getDenseinfo().getUserSid(i))));
+                // Timestamp (delta)
+                node.setTimestamp(new Date(previousTimestamp+=nodes.getDenseinfo().getTimestamp(i)));
+                // A single table contains all keys/values of all nodes.
+                // Each node's tags are encoded in alternating <key_id> <value_id>.
+                // A single stringid of 0 delimit when the tags of a node ends and the tags of the next node begin.
+                Map<String, String> keys = new HashMap<String, String>();
+                while (keyindex < nodes.getKeysValsCount()) {
+                    int key_id = nodes.getKeysVals(keyindex++);
+                    if (key_id == 0) {
+                        break; // End of current node's tags
+                    } else {
+                        int value_id = nodes.getKeysVals(keyindex++);
+                        keys.put(getStringById(key_id), getStringById(value_id));
+                    }
+                }
+                node.setKeys(keys);
+                externalIdMap.put(node.getPrimitiveId(), node);
+            }
+        }
+
+        @Override
+        protected void parseNodes(List<Osmformat.Node> osmNodes) {
+            for (Osmformat.Node n : osmNodes) {
+                Node node = new Node(n.getId(), n.getInfo().getVersion());
+                node.setCoor(new LatLon(parseLat(n.getLat()), parseLon(n.getLon())));
+                node.setChangesetId(n.getInfo().getChangeset());
+                node.setUser(User.createOsmUser(n.getInfo().getUid(), getStringById(n.getInfo().getUserSid())));
+                node.setTimestamp(new Date(n.getInfo().getTimestamp()));
+                Map<String, String> keys = new HashMap<String, String>();
+                for (int i=0; i<n.getKeysCount(); i++) {
+                    keys.put(getStringById(n.getKeys(i)), getStringById(n.getVals(i)));
+                }
+                node.setKeys(keys);
+                externalIdMap.put(node.getPrimitiveId(), node);
+            }
+        }
+        
+        @Override
+        protected void parseWays(List<Osmformat.Way> osmWays) {
+            for (Osmformat.Way w : osmWays) {
+                Way way = new Way(w.getId(), w.getInfo().getVersion());
+                way.setChangesetId(w.getInfo().getChangeset());
+                way.setUser(User.createOsmUser(w.getInfo().getUid(), getStringById(w.getInfo().getUserSid())));
+                way.setTimestamp(new Date(w.getInfo().getTimestamp()));
+                Map<String, String> keys = new HashMap<String, String>();
+                for (int i=0; i<w.getKeysCount(); i++) {
+                    keys.put(getStringById(w.getKeys(i)), getStringById(w.getVals(i)));
+                }
+                way.setKeys(keys);
+                long previousId = 0; // Node ids are delta coded
+                Collection<Long> nodeIds = new ArrayList<Long>();
+                for (Long id : w.getRefsList()) {
+                    nodeIds.add(previousId+=id);
+                }
+                ways.put(way.getUniqueId(), nodeIds);
+                externalIdMap.put(way.getPrimitiveId(), way);
+            }
+        }
+        
+        @Override
+        protected void parseRelations(List<Osmformat.Relation> osmRels) {
+            for (Osmformat.Relation r : osmRels) {
+                Relation rel = new Relation(r.getId(), r.getInfo().getVersion());
+                rel.setChangesetId(r.getInfo().getChangeset());
+                rel.setUser(User.createOsmUser(r.getInfo().getUid(), getStringById(r.getInfo().getUserSid())));
+                rel.setTimestamp(new Date(r.getInfo().getTimestamp()));
+                Map<String, String> keys = new HashMap<String, String>();
+                for (int i=0; i<r.getKeysCount(); i++) {
+                    keys.put(getStringById(r.getKeys(i)), getStringById(r.getVals(i)));
+                }
+                rel.setKeys(keys);
+                long previousId = 0; // Member ids are delta coded
+                Collection<RelationMemberData> members = new ArrayList<RelationMemberData>();
+                for (int i = 0; i<r.getMemidsCount(); i++) {
+                    RelationMemberData rmd = new RelationMemberData();
+                    rmd.id = previousId+=r.getMemids(i);
+                    rmd.role = getStringById(r.getRolesSid(i));
+                    switch (r.getTypes(i)) {
+                        case NODE:
+                            rmd.type = OsmPrimitiveType.NODE;
+                            break;
+                        case WAY:
+                            rmd.type = OsmPrimitiveType.WAY;
+                            break;
+                        case RELATION:
+                            rmd.type = OsmPrimitiveType.RELATION;
+                            break;
+                    }
+                    members.add(rmd);
+                }
+                relations.put(rel.getUniqueId(), members);
+                externalIdMap.put(rel.getPrimitiveId(), rel);
+            }
+        }
+
+        @Override
+        public void complete() {
+        }
+    }
+
+    private PbfParser parser = new PbfParser();
+    
+    /**
+     * Parse the given input source and return the dataset.
+     *
+     * @param source the source input stream. Must not be null.
+     * @param progressMonitor  the progress monitor. If null, {@see NullProgressMonitor#INSTANCE} is assumed
+     *
+     * @return the dataset with the parsed data
+     * @throws IllegalDataException thrown if the an error was found while parsing the data from the source
+     * @throws IllegalArgumentException thrown if source is null
+     */
+    public static DataSet parseDataSet(InputStream source, ProgressMonitor progressMonitor) throws IllegalDataException {
+        if (progressMonitor == null) {
+            progressMonitor = NullProgressMonitor.INSTANCE;
+        }
+        CheckParameterUtil.ensureParameterNotNull(source, "source");
+
+        PbfReader reader = new PbfReader();
+        
+        try {
+            progressMonitor.beginTask(tr("Prepare OSM data...", 2));
+            progressMonitor.indeterminateSubTask(tr("Reading OSM data..."));
+
+            reader.parse(source);
+            progressMonitor.worked(1);
+
+            progressMonitor.indeterminateSubTask(tr("Preparing data set..."));
+            reader.prepareDataSet();
+            progressMonitor.worked(1);
+            return reader.getDataSet();
+        } catch (IllegalDataException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new IllegalDataException(e);
+        } finally {
+            progressMonitor.finishTask();
+        }
+    }
+
+    public void parse(InputStream source) throws IOException {
+        new BlockInputStream(source, parser).process();
+    }
+}
