/******************************************************************************* * Copyright (c) 2008, 2011 Wind River Systems, Inc. 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 * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Markus Schorn - initial API and implementation *******************************************************************************/ package org.eclipse.cdt.internal.core.pdom.indexer; import java.io.File; import java.lang.ref.Reference; import java.lang.ref.SoftReference; import java.net.URI; import java.util.Arrays; import java.util.BitSet; import java.util.HashMap; import java.util.Map; import org.eclipse.cdt.utils.UNCPathConverter; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.IFileStore; import org.eclipse.core.runtime.CoreException; /** * A cache for checking whether a file exists. The cache shall be used for a limited amount of time, only (e.g. one * indexer task). It uses as much memory as it needs. To protect against OutOfMemory situations, a soft reference is * used. * @since 5.0 */ public final class FileExistsCache { private static final Content EMPTY_STRING_ARRAY= new Content(new String[0]); private static boolean BYPASS_CACHE= Boolean.getBoolean("CDT_INDEXER_BYPASS_FILE_EXISTS_CACHE"); //$NON-NLS-1$ private static class Content { public Content(String[] names) { fNames= names; fIsFile= new BitSet(names.length*2); } public String[] fNames; public BitSet fIsFile; } private Reference<Map<String,Content>> fCache= null; private final boolean fCaseInSensitive; public FileExistsCache(boolean caseInsensitive) { fCaseInSensitive= caseInsensitive; fCache= new SoftReference<Map<String,Content>>(new HashMap<String, Content>()); // before running out of memory the entire map will be thrown away. } public boolean isFile(String path) { String parent; String name; File file = null; IFileStore parentStore = null; IFileStore fileStore = null; if (UNCPathConverter.isUNC(path)) { try { URI uri = UNCPathConverter.getInstance().toURI(path); fileStore = EFS.getStore(uri); if (BYPASS_CACHE) { return fileStore != null && !fileStore.fetchInfo().isDirectory(); } parentStore = fileStore.getParent(); if (parentStore == null) { parentStore = fileStore; } parent = parentStore.toURI().toString(); name = fileStore.getName(); } catch (CoreException e) { return false; } } else { file= new File(path); if (BYPASS_CACHE) { return file.isFile(); } parent= file.getParent(); if (parent == null) return false; name= file.getName(); } if (fCaseInSensitive) name= name.toUpperCase(); Content avail= getExistsCache().get(parent); if (avail == null) { String[] files = null; try { files = (parentStore == null) ? new File(parent).list() : parentStore.childNames(EFS.NONE, null); } catch (CoreException e) { // Ignore } if (files == null || files.length == 0) { avail= EMPTY_STRING_ARRAY; } else { if (fCaseInSensitive) { for (int i = 0; i < files.length; i++) { files[i]= files[i].toUpperCase(); } } Arrays.sort(files); avail= new Content(files); } getExistsCache().put(parent, avail); } int idx= Arrays.binarySearch(avail.fNames, name); if (idx < 0) return false; idx *= 2; final BitSet isFileBitset = avail.fIsFile; if (isFileBitset.get(idx)) return true; if (isFileBitset.get(idx+1)) return false; if ((file != null && file.isFile()) || (fileStore != null && !fileStore.fetchInfo().isDirectory())) { isFileBitset.set(idx); return true; } isFileBitset.set(idx+1); return false; } private Map<String, Content> getExistsCache() { Map<String, Content> cache= fCache.get(); if (cache == null) { cache= new HashMap<String, Content>(); fCache= new SoftReference<Map<String, Content>>(cache); // before running out of memory the entire map will be thrown away. } return cache; } }