Index: trunk/src/org/openstreetmap/josm/actions/DownloadAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/DownloadAction.java	(revision 7530)
+++ trunk/src/org/openstreetmap/josm/actions/DownloadAction.java	(revision 7531)
@@ -11,4 +11,5 @@
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.downloadtasks.DownloadGpsTask;
+import org.openstreetmap.josm.actions.downloadtasks.DownloadNotesTask;
 import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask;
 import org.openstreetmap.josm.actions.downloadtasks.PostDownloadHandler;
@@ -54,4 +55,12 @@
                 Main.worker.submit(new PostDownloadHandler(task, future));
             }
+
+            //TODO: This eventually needs to be a checkbox in the UI
+            //For now I'm adding it as a hidden feature since this is still a work in progress
+            if (Main.pref.getBoolean("osm.notes.enableDownload", false)) {
+                DownloadNotesTask task = new DownloadNotesTask();
+                Future<?> future = task.download(false, area, null);
+                Main.worker.submit(new PostDownloadHandler(task, future));
+            }
         }
     }
Index: trunk/src/org/openstreetmap/josm/actions/OpenLocationAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/OpenLocationAction.java	(revision 7530)
+++ trunk/src/org/openstreetmap/josm/actions/OpenLocationAction.java	(revision 7531)
@@ -23,4 +23,5 @@
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.downloadtasks.DownloadGpsTask;
+import org.openstreetmap.josm.actions.downloadtasks.DownloadNotesTask;
 import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmChangeCompressedTask;
 import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmChangeTask;
@@ -59,4 +60,5 @@
         addDownloadTaskClass(DownloadOsmTask.class);
         addDownloadTaskClass(DownloadGpsTask.class);
+        addDownloadTaskClass(DownloadNotesTask.class);
         addDownloadTaskClass(DownloadOsmChangeTask.class);
         addDownloadTaskClass(DownloadOsmUrlTask.class);
Index: trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadNotesTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadNotesTask.java	(revision 7531)
+++ trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadNotesTask.java	(revision 7531)
@@ -0,0 +1,196 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.actions.downloadtasks;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.List;
+import java.util.concurrent.Future;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.notes.Note;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.gui.layer.NoteLayer;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.io.BoundingBoxDownloader;
+import org.openstreetmap.josm.io.OsmServerLocationReader;
+import org.openstreetmap.josm.io.OsmServerReader;
+import org.openstreetmap.josm.io.OsmTransferException;
+import org.xml.sax.SAXException;
+
+/** Task for downloading notes */
+public class DownloadNotesTask extends AbstractDownloadTask {
+
+    private static final String PATTERN_API_URL = "https?://.*/api/0.6/notes.*";
+    private static final String PATTERN_DUMP_FILE = "https?://.*/(.*\\.osn(.bz2)?)";
+
+    private DownloadTask downloadTask;
+
+    @Override
+    public Future<?> download(boolean newLayer, Bounds downloadArea, ProgressMonitor progressMonitor) {
+        downloadTask = new DownloadBoundingBoxTask(new BoundingBoxDownloader(downloadArea), progressMonitor);
+        return Main.worker.submit(downloadTask);
+    }
+
+    @Override
+    public Future<?> loadUrl(boolean newLayer, String url, ProgressMonitor progressMonitor) {
+        if (url.endsWith(".bz2")) {
+            downloadTask = new DownloadBzip2RawUrlTask(new OsmServerLocationReader(url), progressMonitor);
+        } else {
+            downloadTask = new DownloadRawUrlTask(new OsmServerLocationReader(url), progressMonitor);
+        }
+        return Main.worker.submit(downloadTask);
+    }
+
+    @Override
+    public void cancel() {
+        if (downloadTask != null) {
+            downloadTask.cancel();
+        }
+    }
+
+    @Override
+    public String getConfirmationMessage(URL url) {
+        return null;
+    }
+
+    @Override
+    public String getTitle() {
+        return "Download OSM Notes";
+    }
+
+    @Override
+    public String[] getPatterns() {
+        return new String[] {PATTERN_API_URL, PATTERN_DUMP_FILE};
+    }
+
+    abstract class DownloadTask extends PleaseWaitRunnable {
+        protected OsmServerReader reader;
+        protected List<Note> notesData;
+
+        public DownloadTask(OsmServerReader reader, ProgressMonitor progressMonitor) {
+            super(tr("Downloading Notes"));
+            this.reader = reader;
+        }
+
+
+        @Override
+        protected void finish() {
+            Main.debug("finish called in DownloadNotesTask");
+            if (isCanceled() || isFailed()) {
+                Main.debug("was cancelled or failed");
+                return;
+            }
+
+            if (notesData == null) {
+                return;
+            }
+            Main.debug("Notes downloaded: " + notesData.size());
+
+            List<NoteLayer> noteLayers = null;
+            if (Main.map != null) {
+                noteLayers = Main.map.mapView.getLayersOfType(NoteLayer.class);
+            }
+            NoteLayer layer;
+            if (noteLayers != null && noteLayers.size() > 0) {
+                layer = noteLayers.get(0);
+                layer.addNotes(notesData);
+            } else {
+                layer = new NoteLayer(notesData, "Notes");
+                Main.main.addLayer(layer);
+            }
+        }
+
+        @Override
+        protected void cancel() {
+            setCanceled(true);
+            if (reader != null) {
+                reader.cancel();
+            }
+        }
+
+        @Override
+        public abstract void realRun() throws IOException, SAXException, OsmTransferException;
+    }
+
+    class DownloadBoundingBoxTask extends DownloadTask {
+
+        public DownloadBoundingBoxTask(OsmServerReader reader, ProgressMonitor progressMonitor) {
+            super(reader, progressMonitor);
+        }
+
+        @Override
+        public void realRun() throws IOException, SAXException, OsmTransferException {
+            if (isCanceled()) {
+                return;
+            }
+            ProgressMonitor subMonitor = progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false);
+            try {
+                notesData = reader.parseNotes(null, null, subMonitor);
+            } catch (Exception e) {
+                if (isCanceled())
+                    return;
+                if (e instanceof OsmTransferException) {
+                    rememberException(e);
+                } else {
+                    rememberException(new OsmTransferException(e));
+                }
+            }
+        }
+    }
+
+    class DownloadRawUrlTask extends DownloadTask {
+
+        public DownloadRawUrlTask(OsmServerReader reader, ProgressMonitor progressMonitor) {
+            super(reader, progressMonitor);
+        }
+
+        @Override
+        public void realRun() throws IOException, SAXException, OsmTransferException {
+            if (isCanceled()) {
+                return;
+            }
+            ProgressMonitor subMonitor = progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false);
+            try {
+                notesData = reader.parseRawNotes(subMonitor);
+            } catch (Exception e) {
+                if (isCanceled())
+                    return;
+                if (e instanceof OsmTransferException) {
+                    rememberException(e);
+                } else {
+                    rememberException(new OsmTransferException(e));
+                }
+            }
+        }
+    }
+
+    class DownloadBzip2RawUrlTask extends DownloadTask {
+
+        public DownloadBzip2RawUrlTask(OsmServerReader reader, ProgressMonitor progressMonitor) {
+            super(reader, progressMonitor);
+        }
+
+        @Override
+        public void realRun() throws IOException, SAXException, OsmTransferException {
+            if (isCanceled()) {
+                return;
+            }
+            ProgressMonitor subMonitor = progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false);
+            try {
+                notesData = reader.parseRawNotesBzip2(subMonitor);
+            } catch (Exception e) {
+                if (isCanceled())
+                    return;
+                if (e instanceof OsmTransferException) {
+                    rememberException(e);
+                } else {
+                    rememberException(new OsmTransferException(e));
+                }
+            }
+        }
+    }
+
+}
Index: trunk/src/org/openstreetmap/josm/io/BoundingBoxDownloader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/BoundingBoxDownloader.java	(revision 7530)
+++ trunk/src/org/openstreetmap/josm/io/BoundingBoxDownloader.java	(revision 7531)
@@ -6,7 +6,10 @@
 import java.io.IOException;
 import java.io.InputStream;
-
+import java.util.List;
+
+import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.gpx.GpxData;
+import org.openstreetmap.josm.data.notes.Note;
 import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
@@ -154,3 +157,61 @@
         }
     }
+
+    @Override
+    public List<Note> parseNotes(Integer noteLimit, Integer daysClosed, ProgressMonitor progressMonitor) throws OsmTransferException {
+        progressMonitor.beginTask("Downloading notes");
+        noteLimit = checkNoteLimit(noteLimit);
+        daysClosed = checkDaysClosed(daysClosed);
+        String url = new StringBuilder()
+        .append("notes?limit=")
+        .append(noteLimit)
+        .append("&closed=")
+        .append(daysClosed)
+        .append("&bbox=")
+        .append(lon1)
+        .append(",").append(lat1)
+        .append(",").append(lon2)
+        .append(",").append(lat2)
+        .toString();
+        try {
+            InputStream is = getInputStream(url, progressMonitor.createSubTaskMonitor(1, false));
+            NoteReader reader = new NoteReader(is);
+            return reader.parse();
+        } catch (IOException e) {
+            throw new OsmTransferException(e);
+        } catch (SAXException e) {
+            throw new OsmTransferException(e);
+        } finally {
+            progressMonitor.finishTask();
+        }
+    }
+
+    private Integer checkNoteLimit(Integer limit) {
+        if (limit == null) {
+            limit = Main.pref.getInteger("osm.notes.downloadLimit", 1000);
+        }
+        if (limit > 10000) {
+            Main.error("Requested note limit is over API hard limit of 10000. Reducing to 10000.");
+            limit = 10000;
+        }
+        if (limit < 1) {
+            Main.error("Requested note limit is less than 1. Setting to 1.");
+            limit = 1;
+        }
+        Main.debug("returning note limit: " + limit);
+        return limit;
+    }
+
+    private Integer checkDaysClosed(Integer days) {
+        if (days == null) {
+            days = Main.pref.getInteger("osm.notes.daysClosed", 1);
+        }
+        if (days < -1) {
+            Main.error("Requested days closed must be greater than -1");
+            days = -1;
+        }
+        Main.debug("returning days closed: " + days);
+        return days;
+    }
+
 }
Index: trunk/src/org/openstreetmap/josm/io/OsmServerLocationReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmServerLocationReader.java	(revision 7530)
+++ trunk/src/org/openstreetmap/josm/io/OsmServerLocationReader.java	(revision 7531)
@@ -6,7 +6,10 @@
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
 
 import org.openstreetmap.josm.actions.downloadtasks.DownloadGpsTask;
 import org.openstreetmap.josm.data.gpx.GpxData;
+import org.openstreetmap.josm.data.notes.Note;
 import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
@@ -106,4 +109,14 @@
     }
 
+    @Override
+    public List<Note> parseRawNotes(final ProgressMonitor progressMonitor) throws OsmTransferException {
+        return doParse(new NoteParser(progressMonitor, Compression.NONE), progressMonitor);
+    }
+
+    @Override
+    public List<Note> parseRawNotesBzip2(final ProgressMonitor progressMonitor) throws OsmTransferException {
+        return doParse(new NoteParser(progressMonitor, Compression.BZIP2), progressMonitor);
+    }
+
     protected class OsmParser extends Parser<DataSet> {
         protected OsmParser(ProgressMonitor progressMonitor, Compression compression) {
@@ -154,3 +167,21 @@
         }
     }
+
+    protected class NoteParser extends Parser<List<Note>> {
+
+        public NoteParser(ProgressMonitor progressMonitor, Compression compression) {
+            super(progressMonitor, compression);
+        }
+
+        @Override
+        public List<Note> parse() throws OsmTransferException, IllegalDataException, IOException, SAXException {
+            in = getInputStream(url, progressMonitor.createSubTaskMonitor(1, true));
+            if (in == null) {
+                return new ArrayList<Note>();
+            }
+            progressMonitor.subTask(tr("Downloading OSM notes..."));
+            NoteReader reader = new NoteReader(compression.getUncompressedInputStream(in));
+            return reader.parse();
+        }
+    }
 }
Index: trunk/src/org/openstreetmap/josm/io/OsmServerReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmServerReader.java	(revision 7530)
+++ trunk/src/org/openstreetmap/josm/io/OsmServerReader.java	(revision 7531)
@@ -20,4 +20,5 @@
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.gpx.GpxData;
+import org.openstreetmap.josm.data.notes.Note;
 import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
@@ -349,3 +350,37 @@
         return gpxParsedProperly;
     }
+
+    /**
+     * Downloads notes from the API, given API limit parameters
+     *
+     * @param noteLimit How many notes to download. Defaults to 1000 if not specified. API has a hard limit of 10000
+     * @param daysClosed Return notes closed this many days in the past. -1 means all notes, ever. 0 means only unresolved notes.
+     * @param progressMonitor Progress monitor for user feedback
+     * @return List of notes returned by the API
+     * @throws OsmTransferException if any errors happen
+     */
+    public List<Note> parseNotes(Integer noteLimit, Integer daysClosed, ProgressMonitor progressMonitor) throws OsmTransferException {
+        return null;
+    }
+
+    /**
+     * Downloads notes from a given raw URL. The URL is assumed to be complete and no API limits are added
+     *
+     * @param progressMonitor
+     * @return A list of notes parsed from the URL
+     * @throws OsmTransferException
+     */
+    public List<Note> parseRawNotes(final ProgressMonitor progressMonitor) throws OsmTransferException {
+        return null;
+    }
+
+    /**
+     * Download notes from a URL that contains a bzip2 compressed notes dump file
+     * @param progressMonitor
+     * @return A list of notes parsed from the URL
+     * @throws OsmTransferException
+     */
+    public List<Note> parseRawNotesBzip2(final ProgressMonitor progressMonitor) throws OsmTransferException {
+        return null;
+    }
 }
