Code example for LinkedHashMap

Methods: values

0
	private final long maxSize;
	private final int valueCount;
	private long size = 0;
	private Writer journalWriter;
	private final LinkedHashMap<String, Entry> lruEntries = new LinkedHashMap<String, Entry>(0,
			0.75f, true); 
	private int redundantOpCount;
 
	/** 
	 * To differentiate between old and current snapshots, each entry is given a 
	 * sequence number each time an edit is committed. A snapshot is stale if 
	 * its sequence number is not equal to its entry's sequence number. 
	 */ 
	private long nextSequenceNumber = 0;
 
	/* From java.util.Arrays */ 
	@SuppressWarnings("unchecked") 
	private static <T> T[] copyOfRange(T[] original, int start, int end) {
		final int originalLength = original.length; // For exception priority
													// compatibility. 
		if (start > end) {
			throw new IllegalArgumentException();
		} 
		if (start < 0 || start > originalLength) {
			throw new ArrayIndexOutOfBoundsException();
		} 
		final int resultLength = end - start;
		final int copyLength = Math.min(resultLength, originalLength - start);
		final T[] result = (T[]) Array.newInstance(original.getClass().getComponentType(),
				resultLength);
		System.arraycopy(original, start, result, 0, copyLength);
		return result;
	} 
 
	/** 
	 * Returns the ASCII characters up to but not including the next "\r\n", or 
	 * "\n". 
	 *  
	 * @throws java.io.EOFException 
	 *             if the stream is exhausted before the next newline character. 
	 */ 
	public static String readAsciiLine(InputStream in) throws IOException {
		// TODO: support UTF-8 here instead 
 
		StringBuilder result = new StringBuilder(80);
		while (true) { 
			int c = in.read();
			if (c == -1) {
				throw new EOFException();
			} else if (c == '\n') {
				break; 
			} 
 
			result.append((char) c);
		} 
		int length = result.length();
		if (length > 0 && result.charAt(length - 1) == '\r') {
			result.setLength(length - 1);
		} 
		return result.toString();
	} 
 
	/** 
	 * Recursively delete everything in {@code dir}. 
	 */ 
	// TODO: this should specify paths as Strings rather than as Files 
	public static void deleteContents(File dir) throws IOException {
		File[] files = dir.listFiles();
		if (files == null) {
			throw new IllegalArgumentException("not a directory: " + dir);
		} 
		for (File file : files) {
			if (file.isDirectory()) {
				deleteContents(file);
			} 
			if (!file.delete()) {
				throw new IOException("failed to delete file: " + file);
			} 
		} 
	} 
 
	/** This cache uses a single background thread to evict entries. */ 
	private final ExecutorService executorService = new ThreadPoolExecutor(0, 1, 60L,
			TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
	private final Callable<Void> cleanupCallable = new Callable<Void>() {
		@Override 
		public Void call() throws Exception { 
			synchronized (DiskLruCache.this) { 
				if (journalWriter == null) { 
					return null; // closed 
				} 
				trimToSize(); 
				if (journalRebuildRequired()) { 
					rebuildJournal(); 
					redundantOpCount = 0; 
				} 
			} 
			return null; 
		} 
	}; 
 
	private DiskLruCache(File directory, int appVersion, int valueCount, long maxSize) {
		this.directory = directory;
		this.appVersion = appVersion;
		this.journalFile = new File(directory, JOURNAL_FILE);
		this.journalFileTmp = new File(directory, JOURNAL_FILE_TMP);
		this.valueCount = valueCount;
		this.maxSize = maxSize;
	} 
 
	/** 
	 * Opens the cache in {@code directory}, creating a cache if none exists 
	 * there. 
	 *  
	 * @param directory 
	 *            a writable directory 
	 * @param appVersion 
	 * @param valueCount 
	 *            the number of values per cache entry. Must be positive. 
	 * @param maxSize 
	 *            the maximum number of bytes this cache should use to store 
	 * @throws IOException 
	 *             if reading or writing the cache directory fails 
	 */ 
	public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
			throws IOException { 
		if (maxSize <= 0) {
			throw new IllegalArgumentException("maxSize <= 0");
		} 
		if (valueCount <= 0) {
			throw new IllegalArgumentException("valueCount <= 0");
		} 
 
		// prefer to pick up where we left off 
		DiskLruCache cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);
		if (cache.journalFile.exists()) {
			try { 
				cache.readJournal();
				cache.processJournal();
				cache.journalWriter = new BufferedWriter(new FileWriter(cache.journalFile, true),
						IO_BUFFER_SIZE);
				return cache;
			} catch (IOException journalIsCorrupt) {
				// System.logW("DiskLruCache " + directory + " is corrupt: " 
				// + journalIsCorrupt.getMessage() + ", removing"); 
				cache.delete();
			} 
		} 
 
		// create a new empty cache 
		directory.mkdirs();
		cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);
		cache.rebuildJournal();
		return cache;
	} 
 
	private void readJournal() throws IOException { 
		InputStream in = new BufferedInputStream(new FileInputStream(journalFile), IO_BUFFER_SIZE);
		try { 
			String magic = readAsciiLine(in);
			String version = readAsciiLine(in);
			String appVersionString = readAsciiLine(in);
			String valueCountString = readAsciiLine(in);
			String blank = readAsciiLine(in);
			if (!MAGIC.equals(magic) || !VERSION_1.equals(version)
					|| !Integer.toString(appVersion).equals(appVersionString)
					|| !Integer.toString(valueCount).equals(valueCountString) || !"".equals(blank)) {
				throw new IOException("unexpected journal header: [" + magic + ", " + version
						+ ", " + valueCountString + ", " + blank + "]");
			} 
 
			while (true) { 
				try { 
					readJournalLine(readAsciiLine(in));
				} catch (EOFException endOfJournal) {
					break; 
				} 
			} 
		} finally { 
			IOUtils.closeQuietly(in);
		} 
	} 
 
	private void readJournalLine(String line) throws IOException {
		String[] parts = line.split(" ");
		if (parts.length < 2) {
			throw new IOException("unexpected journal line: " + line);
		} 
 
		String key = parts[1];
		if (parts[0].equals(REMOVE) && parts.length == 2) {
			lruEntries.remove(key);
			return; 
		} 
 
		Entry entry = lruEntries.get(key);
		if (entry == null) {
			entry = new Entry(key);
			lruEntries.put(key, entry);
		} 
 
		if (parts[0].equals(CLEAN) && parts.length == 2 + valueCount) {
			entry.readable = true;
			entry.currentEditor = null;
			entry.setLengths(copyOfRange(parts, 2, parts.length));
		} else if (parts[0].equals(DIRTY) && parts.length == 2) {
			entry.currentEditor = new Editor(entry);
		} else if (parts[0].equals(READ) && parts.length == 2) {
			// this work was already done by calling lruEntries.get() 
		} else { 
			throw new IOException("unexpected journal line: " + line);
		} 
	} 
 
	/** 
	 * Computes the initial size and collects garbage as a part of opening the 
	 * cache. Dirty entries are assumed to be inconsistent and will be deleted. 
	 */ 
	private void processJournal() throws IOException { 
		deleteIfExists(journalFileTmp);
		for (Iterator<Entry> i = lruEntries.values().iterator(); i.hasNext();) {
			Entry entry = i.next();
			if (entry.currentEditor == null) {
				for (int t = 0; t < valueCount; t++) {
					size += entry.lengths[t];
				}