Index: src/org/openstreetmap/gui/jmapviewer/OsmFileCacheTileLoader.java
===================================================================
--- src/org/openstreetmap/gui/jmapviewer/OsmFileCacheTileLoader.java	(revision 30322)
+++ src/org/openstreetmap/gui/jmapviewer/OsmFileCacheTileLoader.java	(working copy)
@@ -300,6 +300,7 @@
                     if (fin != null) {
                         fin.close();
                         tileFile.delete();
+                        pruneTileDirectory(tileFile);
                     }
                 } catch (Exception e1) {
                 }
@@ -381,19 +382,34 @@
         }
 
         protected File getTileFile() {
-            return new File(tileCacheDir + "/" + tile.getZoom() + "_" + tile.getXtile() + "_" + tile.getYtile() + "."
+            return new File(tileCacheDir + "/" + tile.getZoom() + "/" + tile.getXtile() + "/" + tile.getYtile() + "."
                     + tile.getSource().getTileType());
         }
 
         protected File getTagsFile() {
-            return new File(tileCacheDir + "/" + tile.getZoom() + "_" + tile.getXtile() + "_" + tile.getYtile()
+            return new File(tileCacheDir + "/" + tile.getZoom() + "/" + tile.getXtile() + "/" + tile.getYtile()
                     + TAGS_FILE_EXT);
         }
 
+        protected void prepareTileDirectory(File path) {
+            new File(path.getParent()).mkdirs();
+        }
+
+        protected void pruneTileDirectory(File path) {
+            // Cleanup Y/X/Z in turn
+            File parent = path;
+            for (int i = 0; i < 3; ++i) {
+              parent = new File(parent.getParent());
+              if (!parent.delete()) break;
+            }
+        }
+
         protected void saveTileToFile(byte[] rawData) {
             try {
-                FileOutputStream f = new FileOutputStream(tileCacheDir + "/" + tile.getZoom() + "_" + tile.getXtile()
-                        + "_" + tile.getYtile() + "." + tile.getSource().getTileType());
+                File file = new File(tileCacheDir + "/" + tile.getZoom() + "/" + tile.getXtile()
+                        + "/" + tile.getYtile() + "." + tile.getSource().getTileType());
+                prepareTileDirectory(file);
+                FileOutputStream f = new FileOutputStream(file);
                 f.write(rawData);
                 f.close();
                 // System.out.println("Saved tile to file: " + tile);
@@ -404,8 +420,10 @@
 
         protected void saveTagsToFile() {
             File tagsFile = getTagsFile();
+            prepareTileDirectory(tagsFile);
             if (tile.getMetadata() == null) {
                 tagsFile.delete();
+                pruneTileDirectory(tagsFile);
                 return;
             }
             try {
@@ -499,22 +517,30 @@
         clearCache(source, null);
     }
 
-    @Override
-    public void clearCache(TileSource source, TileClearController controller) {
-        File dir = getSourceCacheDir(source);
+    public void clearDirectory(TileClearController controller, File dir) throws IOException {
         if (dir != null) {
-            if (controller != null) controller.initClearDir(dir);
-            if (dir.isDirectory()) {
+            if (dir.isDirectory() && dir.getAbsolutePath().equals(dir.getCanonicalPath())) {
+                if (controller != null) controller.initClearDir(dir);
                 File[] files = dir.listFiles();
                 if (controller != null) controller.initClearFiles(files);
                 for (File file : files) {
                     if (controller != null && controller.cancel()) return;
-                    file.delete();
+                    clearDirectory(controller, file);
                     if (controller != null) controller.fileDeleted(file);
                 }
             }
             dir.delete();
+            if (controller != null) controller.fileDeleted(dir);
         }
-        if (controller != null) controller.clearFinished();
     }
+
+    @Override
+    public void clearCache(TileSource source, TileClearController controller) {
+        File dir = getSourceCacheDir(source);
+        try {
+            clearDirectory(controller, dir);
+            if (controller != null) controller.clearFinished();
+        } catch(IOException i) {
+        }
+    }
 }
