/*
Copyright (C) 2001, 2006 United States Government
as represented by the Administrator of the
National Aeronautics and Space Administration.
All Rights Reserved.
*/
package gov.nasa.worldwind.cache;
import gov.nasa.worldwind.util.Logging;
import java.util.logging.Level;
/**
* @author tag
* @version $Id: AbstractFileCache.java 3400 2007-10-28 07:49:25Z tgaskins $
*/
public class AbstractFileCache implements FileCache
{
private final java.util.LinkedList<java.io.File> cacheDirs = new java.util.LinkedList<java.io.File>();
private java.io.File cacheWriteDir = null;
protected void initialize(java.io.InputStream xmlConfigStream)
{
javax.xml.parsers.DocumentBuilderFactory docBuilderFactory =
javax.xml.parsers.DocumentBuilderFactory.newInstance();
try
{
javax.xml.parsers.DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
org.w3c.dom.Document doc = docBuilder.parse(xmlConfigStream);
// The order of the following two calls is important, because building the writable location may entail
// creating a location that's included in the specified read locations.
this.buildWritePaths(doc);
this.buildReadPaths(doc);
if (this.cacheWriteDir == null)
{
Logging.logger().warning("FileCache.NoFileCacheWriteLocation");
}
if (this.cacheDirs.size() == 0)
{
// This should not happen because the writable cache is added to the read list, but check nonetheless
String message = Logging.getMessage("FileCache.NoFileCacheReadLocations");
Logging.logger().severe(message);
throw new IllegalStateException(message);
}
}
catch (javax.xml.parsers.ParserConfigurationException e)
{
String message = Logging.getMessage("FileCache.NoFileCacheReadLocations");
Logging.logger().severe(message);
throw new IllegalStateException(message, e);
}
catch (org.xml.sax.SAXException e)
{
String message = Logging.getMessage("FileCache.NoFileCacheReadLocations");
Logging.logger().severe(message);
throw new IllegalStateException(message, e);
}
catch (java.io.IOException e)
{
String message = Logging.getMessage("FileCache.ExceptionReadingCacheLocationFile");
Logging.logger().severe(message);
throw new IllegalStateException(message, e);
}
}
public void addCacheLocation(String newPath)
{
this.addCacheLocation(this.cacheDirs.size(), newPath);
}
public void addCacheLocation(int index, String newPath)
{
if (newPath == null || newPath.length() == 0)
{
String message = Logging.getMessage("nullValue.FileCachePathIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (index < 0)
{
String message = Logging.getMessage("generic.InvalidIndex", index);
Logging.logger().fine(message);
throw new IllegalArgumentException(message);
}
if (index > 0 && index > this.cacheDirs.size())
index = this.cacheDirs.size();
java.io.File newFile = new java.io.File(newPath);
if (this.cacheDirs.contains(newFile))
this.cacheDirs.remove(newFile);
this.cacheDirs.add(index, newFile);
}
public void removeCacheLocation(String newPath)
{
if (newPath == null || newPath.length() == 0)
{
String message = Logging.getMessage("nullValue.FileCachePathIsNull");
Logging.logger().severe(message);
// Just warn and return.
return;
}
java.io.File newFile = new java.io.File(newPath);
if (newFile.equals(this.cacheWriteDir))
{
String message = Logging.getMessage("FileCache.CannotRemoveWriteLocationFromSearchList", newPath);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
this.cacheDirs.remove(new java.io.File(newPath));
}
public java.util.List<java.io.File> getCacheLocations()
{
// Return a copy.
return new java.util.LinkedList<java.io.File>(this.cacheDirs);
}
public java.io.File getWriteLocation()
{
return this.cacheWriteDir;
}
private void buildReadPaths(org.w3c.dom.Node dataFileCacheNode)
{
javax.xml.xpath.XPathFactory pathFactory = javax.xml.xpath.XPathFactory.newInstance();
javax.xml.xpath.XPath pathFinder = pathFactory.newXPath();
try
{
org.w3c.dom.NodeList locationNodes = (org.w3c.dom.NodeList) pathFinder.evaluate(
"/dataFileCache/readLocations/location",
dataFileCacheNode.getFirstChild(),
javax.xml.xpath.XPathConstants.NODESET);
for (int i = 0; i < locationNodes.getLength(); i++)
{
org.w3c.dom.Node location = locationNodes.item(i);
String prop = pathFinder.evaluate("@property", location);
String wwDir = pathFinder.evaluate("@wwDir", location);
String append = pathFinder.evaluate("@append", location);
String path = buildLocationPath(prop, append, wwDir);
if (path == null)
{
Logging.logger().log(Level.WARNING, "FileCache.CacheLocationInvalid",
prop != null ? prop : Logging.getMessage("generic.Unknown"));
continue;
}
// Even paths that don't exist or are otherwise problematic are added to the list because they may
// become readable during the session. E.g., removable media. So add them to the search list.
java.io.File pathFile = new java.io.File(path);
if (pathFile.exists() && !pathFile.isDirectory())
{
Logging.logger().log(Level.WARNING, "FileCache.CacheLocationIsFile", pathFile.getPath());
}
if (!this.cacheDirs.contains(pathFile)) // filter out duplicates
{
this.cacheDirs.add(pathFile);
}
}
}
catch (javax.xml.xpath.XPathExpressionException e)
{
String message = Logging.getMessage("FileCache.NoFileCacheReadLocations");
Logging.logger().severe(message);
throw new IllegalStateException(message, e);
}
}
private void buildWritePaths(org.w3c.dom.Node dataFileCacheNode)
{
javax.xml.xpath.XPathFactory pathFactory = javax.xml.xpath.XPathFactory.newInstance();
javax.xml.xpath.XPath pathFinder = pathFactory.newXPath();
try
{
org.w3c.dom.NodeList locationNodes = (org.w3c.dom.NodeList) pathFinder.evaluate(
"/dataFileCache/writeLocations/location",
dataFileCacheNode.getFirstChild(),
javax.xml.xpath.XPathConstants.NODESET);
for (int i = 0; i < locationNodes.getLength(); i++)
{
org.w3c.dom.Node location = locationNodes.item(i);
String prop = pathFinder.evaluate("@property", location);
String wwDir = pathFinder.evaluate("@wwDir", location);
String append = pathFinder.evaluate("@append", location);
String create = pathFinder.evaluate("@create", location);
String path = buildLocationPath(prop, append, wwDir);
if (path == null)
{
Logging.logger().log(Level.WARNING, "FileCache.CacheLocationInvalid",
prop != null ? prop : Logging.getMessage("generic.Unknown"));
continue;
}
Logging.logger().log(Level.FINER, "FileCache.AttemptingWriteCache", path);
java.io.File pathFile = new java.io.File(path);
if (!pathFile.exists() && create != null && (create.contains("t") || create.contains("T")))
{
Logging.logger().log(Level.FINER, "FileCache.MakingDirsFor", path);
pathFile.mkdirs();
}
if (pathFile.isDirectory() && pathFile.canWrite() && pathFile.canRead())
{
Logging.logger().log(Level.FINER, "FileCache.WriteCacheSuccessful", path);
this.cacheWriteDir = pathFile;
this.cacheDirs.addFirst(pathFile); // writable location is always first in search path
break; // only need one
}
}
}
catch (javax.xml.xpath.XPathExpressionException e)
{
String message = Logging.getMessage("FileCache.NoFileCacheReadLocations");
Logging.logger().severe(message);
throw new IllegalStateException(message, e);
}
}
private static String buildLocationPath(String property, String append, String wwDir)
{
String path = propertyToPath(property);
if (append != null && append.length() != 0)
path = appendPathPart(path, append.trim());
if (wwDir != null && wwDir.length() != 0)
path = appendPathPart(path, wwDir.trim());
return path;
}
private static String appendPathPart(String firstPart, String secondPart)
{
if (secondPart == null || secondPart.length() == 0)
return firstPart;
if (firstPart == null || secondPart.length() == 0)
return secondPart;
firstPart = stripTrailingSeparator(firstPart);
secondPart = stripLeadingSeparator(secondPart);
return firstPart + System.getProperty("file.separator") + secondPart;
}
private static String stripTrailingSeparator(String s)
{
if (s.endsWith("/") || s.endsWith("\\"))
return s.substring(0, s.length() - 1);
else
return s;
}
private static String stripLeadingSeparator(String s)
{
if (s.startsWith("/") || s.startsWith("\\"))
return s.substring(1, s.length());
else
return s;
}
private static String propertyToPath(String propName)
{
if (propName == null || propName.length() == 0)
return null;
String prop = System.getProperty(propName);
if (prop != null)
return prop;
if (propName.equalsIgnoreCase("gov.nasa.worldwind.platform.alluser.cache"))
return determineAllUserCacheDir();
if (propName.equalsIgnoreCase("gov.nasa.worldwind.platform.user.cache"))
return determineSingleUserCacheDir();
return null;
}
private static String determineAllUserCacheDir()
{
if (gov.nasa.worldwind.Configuration.isMacOS())
{
return "/Library/Caches";
}
else if (gov.nasa.worldwind.Configuration.isWindowsOS())
{
String path = System.getenv("ALLUSERSPROFILE");
if (path == null)
{
Logging.logger().severe("generic.AllUsersWindowsProfileNotKnown");
return null;
}
return path + "\\Application Data";
}
else if (gov.nasa.worldwind.Configuration.isLinuxOS() || gov.nasa.worldwind.Configuration.isUnixOS()
|| gov.nasa.worldwind.Configuration.isSolarisOS())
{
return "/var/cache/";
}
else
{
Logging.logger().warning("generic.UnknownOperatingSystem");
return null;
}
}
private static String determineSingleUserCacheDir()
{
String home = getUserHomeDir();
if (home == null)
{
Logging.logger().warning("generic.UsersHomeDirectoryNotKnown");
return null;
}
String path = null;
if (gov.nasa.worldwind.Configuration.isMacOS())
{
path = "/Library/Caches";
}
else if (gov.nasa.worldwind.Configuration.isWindowsOS())
{
path = System.getenv("USERPROFILE");
if (path == null)
{
Logging.logger().fine("generic.UsersWindowsProfileNotKnown");
return null;
}
path += "\\Application Data";
}
else if (gov.nasa.worldwind.Configuration.isLinuxOS() || gov.nasa.worldwind.Configuration.isUnixOS()
|| gov.nasa.worldwind.Configuration.isSolarisOS())
{
path = "/var/cache/";
}
else
{
Logging.logger().fine("generic.UnknownOperatingSystem");
}
if (path == null)
return null;
return home + path;
}
private static String getUserHomeDir()
{
return System.getProperty("user.home");
}
public boolean contains(String fileName)
{
if (fileName == null)
return false;
for (java.io.File cacheDir : this.cacheDirs)
{
java.io.File file;
if (fileName.startsWith(cacheDir.getAbsolutePath()))
file = new java.io.File(fileName);
else
file = this.cachePathForFile(cacheDir, fileName);
if (file.exists())
return true;
}
return false;
}
private java.io.File cachePathForFile(java.io.File file, String fileName)
{
return new java.io.File(file.getAbsolutePath() + "/" + fileName);
}
private String makeFullPath(java.io.File dir, String fileName)
{
return dir.getAbsolutePath() + "/" + fileName;
}
/**
* @param fileName the name to give the newly created file
* @return a handle to the newly created file if it could be created and added to the cache, otherwise null
* @throws IllegalArgumentException if <code>fileName</code> is null
*/
public java.io.File newFile(String fileName)
{
if (fileName == null)
{
String message = Logging.getMessage("nullValue.FilePathIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (this.cacheWriteDir != null)
{
String fullPath = this.makeFullPath(this.cacheWriteDir, fileName);
java.io.File file = new java.io.File(fullPath);
if (file.getParentFile().exists())
return file;
else if (file.getParentFile().mkdirs())
return file;
else
{
String msg = Logging.getMessage("generic.CantCreateCacheFile", fullPath);
Logging.logger().severe(msg);
}
}
return null;
}
/**
* @param fileName the name of the file to find
* @param checkClassPath if <code>true</code>, the class path is first searched for the file, otherwise the class
* path is not searched unless it's one of the explicit paths in the cache search directories
* @return a handle to the requested file if it exists in the cache, otherwise null
* @throws IllegalArgumentException if <code>fileName</code> is null
*/
public java.net.URL findFile(String fileName, boolean checkClassPath)
{
if (fileName == null)
{
String message = Logging.getMessage("nullValue.FilePathIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (checkClassPath)
{
java.net.URL url = this.getClass().getClassLoader().getResource(fileName);
if (url != null)
return url;
}
for (java.io.File dir : this.cacheDirs)
{
if (!dir.exists())
continue;
java.io.File file = new java.io.File(this.makeFullPath(dir, fileName));
if (file.exists())
{
try
{
return file.toURI().toURL();
}
catch (java.net.MalformedURLException e)
{
Logging.logger().log(Level.SEVERE,
Logging.getMessage("FileCache.ExceptionCreatingURLForFile", file.getPath()), e);
}
}
}
return null;
}
/**
* @param url the "file:" URL of the file to remove from the cache
* @throws IllegalArgumentException if <code>url</code> is null
*/
public void removeFile(java.net.URL url)
{
if (url == null)
{
String msg = Logging.getMessage("nullValue.URLIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
try
{
java.io.File file = new java.io.File(url.toURI());
if (file.exists())
file.delete();
}
catch (java.net.URISyntaxException e)
{
Logging.logger().log(Level.SEVERE, Logging.getMessage("FileCache.ExceptionRemovingFile", url.toString()),
e);
}
}
}