/* * This file is part of muCommander, http://www.mucommander.com * Copyright (C) 2002-2016 Maxence Bernard * * muCommander is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * muCommander is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.mucommander.ui.main.tree; import java.util.Arrays; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.SwingUtilities; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.mucommander.commons.file.AbstractFile; import com.mucommander.commons.file.ProxyFile; import com.mucommander.ui.icon.CustomFileIconProvider; import com.mucommander.ui.icon.FileIcons; import com.mucommander.ui.icon.IconManager; /** * A class that holds cached children of a directory. * * @author Mariusz Jakubowski * */ public class CachedDirectory extends ProxyFile { private static final Logger LOGGER = LoggerFactory.getLogger(CachedDirectory.class); private static final ImageIcon NOT_ACCESSIBLE_ICON = IconManager.getIcon(IconManager.FILE_ICON_SET, CustomFileIconProvider.NOT_ACCESSIBLE_FILE); /** an array of cached children */ private AbstractFile[] cachedChildren = null; /** a flag indicating that a thread is running, caching children */ private boolean readingChildren = false; /** a timestamp of last modification time of this directory */ private long lsTimeStamp = -1; /** a cache in which this object is stored */ private DirectoryCache cache; /** a cached icon */ private Icon cachedIcon; /** * Creates a new instance. * * @param directory a directory to cache */ public CachedDirectory(AbstractFile directory, DirectoryCache cache) { super(directory); this.cache = cache; } /** * Checks if this directory is already cached. If it isn't cached then a new * cache thread is started. * @return true if directory is cached, false otherwise */ public synchronized boolean isCached() { // check if caching thread is running if (isReadingChildren()) { return false; } // check if directory contents changed if (lsTimeStamp != file.getDate()) { setReadingChildren(true); // read children in caching thread TreeIOThreadManager.getInstance().addTask(new Runnable() { public void run() { lsAsync(); } }); return false; } return true; } /** * Gets children of current directory. Files are filtered and then sorted. This * method is executed in caching thread. */ private void lsAsync() { if (getCachedIcon() == null || getCachedIcon() == NOT_ACCESSIBLE_ICON) { setCachedIcon(FileIcons.getFileIcon(getProxiedFile())); } AbstractFile[] children; try { children = file.ls(cache.getFilter()); } catch (Exception e) { LOGGER.debug("Caught exception", e); children = new AbstractFile[0]; setCachedIcon(NOT_ACCESSIBLE_ICON); } Arrays.sort(children, cache.getSort()); Icon icons[] = new Icon[children.length]; for (int i = 0; i < children.length; i++) { icons[i] = FileIcons.getFileIcon(children[i]); } synchronized (cache) { for (int i = 0; i < children.length; i++) { CachedDirectory cachedChild = cache.getOrAdd(children[i]); cachedChild.setCachedIcon(icons[i]); } } final AbstractFile[] children2 = children; try { /* * Set cache to new value. This is invoked in swing thread * so event listeners are called from right thread. */ SwingUtilities.invokeAndWait(new Runnable() { public void run() { setLsCache(children2, file.getDate()); } }); } catch (Exception e) { LOGGER.debug("Caught exception", e); } } /** * Sets cache information. * @param children array of children of this directory * @param lsTimeStamp timestamp of cache */ private synchronized void setLsCache(AbstractFile[] children, long lsTimeStamp) { this.lsTimeStamp = lsTimeStamp; this.cachedChildren = children; setReadingChildren(false); } /** * Returns true if caching thread is running. */ public synchronized boolean isReadingChildren() { return readingChildren; } /** * Sets a flag that indicates if caching thread is running. This method also * initializes spinning icon. * @param readingChildren */ private synchronized void setReadingChildren(boolean readingChildren) { this.readingChildren = readingChildren; cache.fireChildrenCached(this, readingChildren); } /** * Gets cached children. * @return cached children. */ public synchronized AbstractFile[] get() { return cachedChildren; } /** * Gets a cached icon for this folder. * @return a cached icon */ public Icon getCachedIcon() { return cachedIcon; } /** * Sets a cached icon for this folder. * @param cachedIcon a cached icon */ public void setCachedIcon(Icon cachedIcon) { this.cachedIcon = cachedIcon; } }