Index: trunk/src/org/openstreetmap/josm/data/imagery/TemplatedWMSTileSource.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/imagery/TemplatedWMSTileSource.java	(revision 8646)
+++ trunk/src/org/openstreetmap/josm/data/imagery/TemplatedWMSTileSource.java	(revision 8647)
@@ -42,17 +42,18 @@
     private Bounds worldBounds;
 
-    private static final String PATTERN_HEADER  = "\\{header\\(([^,]+),([^}]+)\\)\\}";
-    private static final String PATTERN_PROJ    = "\\{proj(\\([^})]+\\))?\\}";
-    private static final String PATTERN_BBOX    = "\\{bbox\\}";
-    private static final String PATTERN_W       = "\\{w\\}";
-    private static final String PATTERN_S       = "\\{s\\}";
-    private static final String PATTERN_E       = "\\{e\\}";
-    private static final String PATTERN_N       = "\\{n\\}";
-    private static final String PATTERN_WIDTH   = "\\{width\\}";
-    private static final String PATTERN_HEIGHT  = "\\{height\\}";
+    private static final Pattern PATTERN_HEADER  = Pattern.compile("\\{header\\(([^,]+),([^}]+)\\)\\}");
+    private static final Pattern PATTERN_PROJ    = Pattern.compile("\\{proj\\}");
+    private static final Pattern PATTERN_BBOX    = Pattern.compile("\\{bbox\\}");
+    private static final Pattern PATTERN_W       = Pattern.compile("\\{w\\}");
+    private static final Pattern PATTERN_S       = Pattern.compile("\\{s\\}");
+    private static final Pattern PATTERN_E       = Pattern.compile("\\{e\\}");
+    private static final Pattern PATTERN_N       = Pattern.compile("\\{n\\}");
+    private static final Pattern PATTERN_WIDTH   = Pattern.compile("\\{width\\}");
+    private static final Pattern PATTERN_HEIGHT  = Pattern.compile("\\{height\\}");
+    private static final Pattern PATTERN_PARAM   = Pattern.compile("\\{([^}]+)\\}");
 
     private static final NumberFormat latLonFormat = new DecimalFormat("###0.0000000", new DecimalFormatSymbols(Locale.US));
 
-    private static final String[] ALL_PATTERNS = {
+    private static final Pattern[] ALL_PATTERNS = {
         PATTERN_HEADER, PATTERN_PROJ, PATTERN_BBOX, PATTERN_W, PATTERN_S, PATTERN_E, PATTERN_N, PATTERN_WIDTH, PATTERN_HEIGHT
     };
@@ -67,4 +68,7 @@
         handleTemplate();
         initProjection();
+        // FIXME: remove in September 2015, when ImageryPreferenceEntry.tileSize will be initialized to -1 instead to 256
+        // need to leave it as it is to keep compatiblity between tested and latest JOSM versions
+        tileSize = WMSLayer.PROP_IMAGE_SIZE.get();
     }
 
@@ -89,11 +93,4 @@
     @Override
     public int getDefaultTileSize() {
-        return WMSLayer.PROP_IMAGE_SIZE.get();
-    }
-
-    // FIXME: remove in September 2015, when ImageryPreferenceEntry.tileSize will be initialized to -1 instead to 256
-    // need to leave it as it is to keep compatiblity between tested and latest JOSM versions
-    @Override
-    public int getTileSize() {
         return WMSLayer.PROP_IMAGE_SIZE.get();
     }
@@ -156,14 +153,40 @@
             bbox = String.format("%s,%s,%s,%s", latLonFormat.format(w), latLonFormat.format(s), latLonFormat.format(e), latLonFormat.format(n));
         }
-        return baseUrl.
-                replaceAll(PATTERN_PROJ,    myProjCode)
-                .replaceAll(PATTERN_BBOX,   bbox)
-                .replaceAll(PATTERN_W,      latLonFormat.format(w))
-                .replaceAll(PATTERN_S,      latLonFormat.format(s))
-                .replaceAll(PATTERN_E,      latLonFormat.format(e))
-                .replaceAll(PATTERN_N,      latLonFormat.format(n))
-                .replaceAll(PATTERN_WIDTH,  String.valueOf(getTileSize()))
-                .replaceAll(PATTERN_HEIGHT, String.valueOf(getTileSize()))
-                .replace(" ", "%20");
+
+        // Using StringBuffer and generic PATTERN_PARAM matcher gives 2x performance improvement over replaceAll
+        StringBuffer url = new StringBuffer(baseUrl.length());
+        Matcher matcher = PATTERN_PARAM.matcher(baseUrl);
+        while (matcher.find()) {
+            String replacement;
+            switch (matcher.group(1)) {
+            case "proj":
+                replacement = myProjCode;
+                break;
+            case "bbox":
+                replacement = bbox;
+                break;
+            case "w":
+                replacement = latLonFormat.format(w);
+                break;
+            case "s":
+                replacement = latLonFormat.format(s);
+                break;
+            case "e":
+                replacement = latLonFormat.format(e);
+                break;
+            case "n":
+                replacement = latLonFormat.format(n);
+                break;
+            case "width":
+            case "height":
+                replacement = String.valueOf(getTileSize());
+                break;
+            default:
+                replacement = "{" + matcher.group(1) + "}";
+            }
+            matcher.appendReplacement(url, replacement);
+        }
+        matcher.appendTail(url);
+        return url.toString().replace(" ", "%20");
     }
 
@@ -313,9 +336,9 @@
     public static void checkUrl(String url) {
         CheckParameterUtil.ensureParameterNotNull(url, "url");
-        Matcher m = Pattern.compile("\\{[^}]*\\}").matcher(url);
+        Matcher m = PATTERN_PARAM.matcher(url);
         while (m.find()) {
             boolean isSupportedPattern = false;
-            for (String pattern : ALL_PATTERNS) {
-                if (m.group().matches(pattern)) {
+            for (Pattern pattern : ALL_PATTERNS) {
+                if (pattern.matcher(m.group()).matches()) {
                     isSupportedPattern = true;
                     break;
@@ -331,7 +354,6 @@
     private void handleTemplate() {
         // Capturing group pattern on switch values
-        Pattern pattern = Pattern.compile(PATTERN_HEADER);
         StringBuffer output = new StringBuffer();
-        Matcher matcher = pattern.matcher(this.baseUrl);
+        Matcher matcher = PATTERN_HEADER.matcher(this.baseUrl);
         while (matcher.find()) {
             headers.put(matcher.group(1), matcher.group(2));
Index: trunk/src/org/openstreetmap/josm/gui/layer/AbstractCachedTileSourceLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/AbstractCachedTileSourceLayer.java	(revision 8646)
+++ trunk/src/org/openstreetmap/josm/gui/layer/AbstractCachedTileSourceLayer.java	(revision 8647)
@@ -89,5 +89,5 @@
         try {
             cache = JCSCacheManager.getCache(getCacheName(),
-                    getMemoryCacheSize(),
+                    0,
                     getDiskCacheSize(),
                     CachedTileLoaderFactory.PROP_TILECACHE_DIR.get());
@@ -126,6 +126,6 @@
             try {
                 return JCSCacheManager.getCache(name,
-                        MEMORY_CACHE_SIZE.get(),
-                        MAX_DISK_CACHE_SIZE.get() * 1024, // MAX_DISK_CACHE_SIZE is in MB
+                        0,
+                        MAX_DISK_CACHE_SIZE.get() * 1024, // MAX_DISK_CACHE_SIZE is in MB, needs to by in sync with getDiskCacheSize
                         CachedTileLoaderFactory.PROP_TILECACHE_DIR.get());
             } catch (IOException e) {
@@ -137,8 +137,4 @@
     protected abstract Class<? extends TileLoader> getTileLoaderClass();
 
-    protected int getMemoryCacheSize() {
-        return MEMORY_CACHE_SIZE.get();
-    }
-
     protected int getDiskCacheSize() {
         return MAX_DISK_CACHE_SIZE.get() * 1024;
Index: trunk/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java	(revision 8646)
+++ trunk/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java	(revision 8647)
@@ -174,14 +174,14 @@
 
         tileLoader = getTileLoaderFactory().makeTileLoader(this, headers);
-        if (tileLoader instanceof TMSCachedTileLoader) {
-            tileCache = (TileCache) tileLoader;
-        } else {
-            tileCache = new MemoryTileCache();
-        }
+        /*
+         *  use MemoryTileCache instead of tileLoader JCS cache, as tileLoader caches only content (byte[] of image)
+         *  and MemoryTileCache caches whole Tile. This gives huge performance improvement when a lot of tiles are visible
+         *  in MapView (for example - when limiting min zoom in imagery)
+         */
+        tileCache = new MemoryTileCache(AbstractCachedTileSourceLayer.MEMORY_CACHE_SIZE.get());
 
         try {
             if ("file".equalsIgnoreCase(new URL(tileSource.getBaseUrl()).getProtocol())) {
                 tileLoader = new OsmTileLoader(this);
-                tileCache = new MemoryTileCache();
             }
         } catch (MalformedURLException e) {
@@ -1484,16 +1484,11 @@
         //g.drawString("currentZoomLevel=" + currentZoomLevel, 120, 120);
         g.setColor(Color.lightGray);
-        if (!autoZoom) {
-            if (ts.insane()) {
-                myDrawString(g, tr("zoom in to load any tiles"), 120, 120);
-            } else if (ts.tooLarge()) {
-                myDrawString(g, tr("zoom in to load more tiles"), 120, 120);
-            } else if (ts.tooSmall()) {
-                myDrawString(g, tr("increase zoom level to see more detail"), 120, 120);
-            }
-        }
-
-        if (zoom < getMinZoomLvl() && (ts.insane() || ts.tooLarge())) {
+
+        if (ts.insane()) {
             myDrawString(g, tr("zoom in to load any tiles"), 120, 120);
+        } else if (ts.tooLarge()) {
+            myDrawString(g, tr("zoom in to load more tiles"), 120, 120);
+        } else if (!autoZoom && ts.tooSmall()) {
+            myDrawString(g, tr("increase zoom level to see more detail"), 120, 120);
         }
 
