[nobug] get file contents once instead of twice
diff --git a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/util/FileContentCache.java b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/util/FileContentCache.java
index 2802c74..6a29258 100644
--- a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/util/FileContentCache.java
+++ b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/util/FileContentCache.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2007, 2009 IBM Corporation and others.
+ * Copyright (c) 2007, 2012 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -11,15 +11,21 @@
  *******************************************************************************/
 package org.eclipse.jst.jsp.core.internal.util;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
 import java.lang.ref.Reference;
 import java.lang.ref.SoftReference;
-import java.util.HashMap;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.IllegalCharsetNameException;
+import java.nio.charset.UnsupportedCharsetException;
 import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.Map;
 
 import org.eclipse.core.filebuffers.FileBuffers;
@@ -30,21 +36,20 @@
 import org.eclipse.core.resources.ResourcesPlugin;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IPath;
-import org.eclipse.core.runtime.NullProgressMonitor;
 import org.eclipse.core.runtime.Platform;
 import org.eclipse.core.runtime.QualifiedName;
 import org.eclipse.core.runtime.content.IContentDescription;
+import org.eclipse.jface.text.IDocument;
 import org.eclipse.jst.jsp.core.internal.Logger;
-import org.eclipse.wst.sse.core.internal.util.Debug;
 
 public class FileContentCache {
 	private static class CacheEntry {
 		String contents;
 		long modificationStamp = IResource.NULL_STAMP;
-		IPath path;
+		IPath contentPath;
 
 		CacheEntry(IPath path) {
-			this.path = path;
+			this.contentPath = path;
 			modificationStamp = getModificationStamp(path);
 			contents = readContents(path);
 		}
@@ -60,48 +65,24 @@
 			if (modificationStamp == IResource.NULL_STAMP) {
 				return true;
 			}
-			long newStamp = getModificationStamp(path);
+			long newStamp = getModificationStamp(contentPath);
 			return newStamp > modificationStamp;
 		}
 
-		private String detectCharset(IFile file) {
-			if (file.getType() == IResource.FILE && file.isAccessible()) {
-				IContentDescription d = null;
-				try {
-					// optimized description lookup, might not succeed
-					d = file.getContentDescription();
-					if (d != null)
-						return d.getCharset();
-				}
-				catch (CoreException e) {
-					// should not be possible given the accessible and file
-					// type
-					// check above
-				}
-				InputStream contents = null;
-				try {
-					contents = file.getContents();
-					IContentDescription description = Platform.getContentTypeManager().getDescriptionFor(contents, file.getName(), new QualifiedName[]{IContentDescription.CHARSET});
-					if (description != null) {
-						return description.getCharset();
-					}
-				}
-				catch (IOException e) {
-					// will try to cleanup in finally
-				}
-				catch (CoreException e) {
-					// out of sync
-				}
-				finally {
-					if (contents != null) {
-						try {
-							contents.close();
-						}
-						catch (Exception e) {
-							// not sure how to recover at this point
-						}
-					}
-				}
+
+		/**
+		 * @param name
+		 * @param contents2
+		 * @return
+		 */
+		private String detectCharset(String name, byte[] contents) throws IOException {
+			IContentDescription description = Platform.getContentTypeManager().getDescriptionFor(new ByteArrayInputStream(contents), name, new QualifiedName[]{IContentDescription.CHARSET});
+			if (description != null) {
+				String charset = description.getCharset();
+				if (charset == null && description.getContentType() != null)
+					charset = description.getContentType().getDefaultCharset();
+				if (charset != null)
+					return charset;
 			}
 			return ResourcesPlugin.getEncoding();
 		}
@@ -117,35 +98,51 @@
 			return IResource.NULL_STAMP;
 		}
 
+		/**
+		 * Read once and store the contents to determine the encoding and reuse as input
+		 */
 		private String readContents(IPath filePath) {
 			if (DEBUG)
-				System.out.println("readContents:" + filePath);
-			StringBuffer s = new StringBuffer();
+				System.out.println(getClass().getName() + " readContents: " + filePath); //$NON-NLS-1$
+			
 			InputStream is = null;
 			try {
 				IFile f = getFile(filePath);
 				if (f != null && f.isAccessible()) {
-					String charset = detectCharset(f);
-					if (charset == null) {
-						charset = ResourcesPlugin.getEncoding();
-					}
 					is = f.getContents();
-					Reader reader = new InputStreamReader(is, charset);
-					char[] readBuffer = new char[8092];
-					int n = reader.read(readBuffer);
-					while (n > 0) {
-						s.append(readBuffer, 0, n);
-						n = reader.read(readBuffer);
-					}
+				}
+				else {
+					is = new FileInputStream(filePath.toFile());
+				}
+
+				ByteArrayOutputStream store = new ByteArrayOutputStream();
+				byte[] readBuffer = new byte[8092];
+				int n = is.read(readBuffer);
+				while (n > 0) {
+					store.write(readBuffer, 0, n);
+					n = is.read(readBuffer);
+				}
+				
+				byte[] bytes = store.toByteArray();
+				String charset = detectCharset(filePath.lastSegment(), bytes);
+				ByteBuffer buffer = ByteBuffer.wrap(bytes);
+				try {
+					CharBuffer charBuffer = Charset.forName(charset).decode(buffer);
+					return charBuffer.toString();
+				}
+				catch (IllegalCharsetNameException e) {
+					return new String(bytes, charset);
+				}
+				catch (UnsupportedCharsetException e) {
+					return new String(bytes, charset);
 				}
 			}
 			catch (CoreException e) {
+				Logger.logException(e);
 				// out of sync
 			}
 			catch (Exception e) {
-				if (Debug.debugStructuredDocument) {
-					Logger.logException(e);
-				}
+//				Logger.logException(e);
 			}
 			finally {
 				try {
@@ -157,28 +154,7 @@
 					// nothing to do
 				}
 			}
-			if (is == null) {
-				try {
-					FileBuffers.getTextFileBufferManager().connect(filePath, LocationKind.LOCATION, new NullProgressMonitor());
-					ITextFileBuffer buffer = FileBuffers.getTextFileBufferManager().getTextFileBuffer(filePath, LocationKind.LOCATION);
-					if (buffer != null) {
-						s.append(buffer.getDocument().get());
-					}
-				}
-				catch (CoreException e) {
-					// nothing to do
-					Logger.logException(e);
-				}
-				finally {
-					try {
-						FileBuffers.getTextFileBufferManager().disconnect(filePath, LocationKind.LOCATION, new NullProgressMonitor());
-					}
-					catch (CoreException e) {
-						Logger.logException(e);
-					}
-				}
-			}
-			return s.toString();
+			return null;
 		}
 
 	}
@@ -191,11 +167,18 @@
 		return instance;
 	}
 
-	private HashMap fContentMap;
+	static class LimitedHashMap extends LinkedHashMap {
+		private static final long serialVersionUID = 1L;
+
+		protected boolean removeEldestEntry(java.util.Map.Entry eldest) {
+			return size() > 25; //completely arbitrary number
+		}
+	}
+	private LinkedHashMap fContentMap;
 
 	private FileContentCache() {
 		super();
-		fContentMap = new HashMap();
+		fContentMap = new LimitedHashMap();
 	}
 
 	private void cleanup() {
@@ -212,7 +195,28 @@
 
 	public String getContents(IPath filePath) {
 		if (DEBUG)
-			System.out.println("getContents:" + filePath);
+			System.out.println(getClass().getName() + "#getContents: " + filePath); //$NON-NLS-1$
+
+		/*
+		 * Use an open file buffer if one exists for contents in dirty
+		 * editors. LocationKind.IFILE will only apply to workspace paths, but
+		 * they're far more likely to be open in an editor and modified than
+		 * files outside the workspace (LocationKind.LOCATION). We'll use
+		 * LocationKind.NORMALIZE and let the framework sort things out,
+		 * unless it proves to be a performance problem.
+		 * 
+		 * Do not cause a file buffer to be opened by calling connect()
+		 */
+		ITextFileBuffer existingBuffer = FileBuffers.getTextFileBufferManager().getTextFileBuffer(
+				filePath, LocationKind.NORMALIZE);
+		if (existingBuffer != null) {
+			IDocument document = existingBuffer.getDocument();
+			if (document != null) {
+				return document.get();
+			}
+		}
+
+		//check the cache
 		CacheEntry entry = null;
 		Object o = fContentMap.get(filePath);
 		if (o instanceof Reference) {
@@ -220,7 +224,7 @@
 		}
 		if (entry == null || entry.isStale()) {
 			if (DEBUG && entry != null && entry.isStale())
-				System.out.println("stale contents:" + filePath);
+				System.out.println(getClass().getName() + " stale contents: " + filePath); //$NON-NLS-1$
 			entry = new CacheEntry(filePath);
 			synchronized (fContentMap) {
 				fContentMap.put(filePath, new SoftReference(entry));
@@ -229,6 +233,4 @@
 		cleanup();
 		return entry.contents;
 	}
-
-
 }