/* * Open Source Physics software is free software as described near the bottom of * this code file. * * For additional information and documentation on Open Source Physics please * see: <http://www.opensourcephysics.org/> */ package org.opensourcephysics.tools; import java.applet.AudioClip; import java.awt.Component; import java.awt.Image; import java.awt.Toolkit; import java.awt.image.BufferedImage; import java.io.BufferedInputStream; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipInputStream; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import org.opensourcephysics.controls.OSPLog; import org.opensourcephysics.controls.XML; import org.opensourcephysics.display.OSPRuntime; /** * This defines static methods for loading resources. * * @author Douglas Brown * @version 1.0 */ public class ResourceLoader { @SuppressWarnings("javadoc") public static final FileFilter OSP_CACHE_FILTER; protected static final String WIN_XP_DEFAULT_CACHE = "/Local Settings/Application Data/OSP/Cache"; //$NON-NLS-1$ protected static final String WINDOWS_DEFAULT_CACHE = "/AppData/Local/OSP/Cache"; //$NON-NLS-1$ protected static final String OSX_DEFAULT_CACHE = "/Library/Caches/OSP"; //$NON-NLS-1$ protected static final String LINUX_DEFAULT_CACHE = "/Downloads/OSP/Cache"; //$NON-NLS-1$ protected static final String SEARCH_CACHE_SUBDIRECTORY = "Search"; //$NON-NLS-1$ protected static ArrayList<String> searchPaths = new ArrayList<String>(); // search paths protected static ArrayList<String> appletSearchPaths = new ArrayList<String>(); // search paths for apples protected static int maxPaths = 20; // max number of paths in history protected static Hashtable<String, Resource> resources = new Hashtable<String, Resource>(); // cached resources protected static boolean cacheEnabled=false, canceled=false; protected static Map<String, URLClassLoader> zipLoaders = new TreeMap<String, URLClassLoader>(); // maps path to zipLoader protected static URLClassLoader xsetZipLoader; // zipLoader of current xset protected static Set<String> extractExtensions = new TreeSet<String>(); protected static ArrayList<String> pathsNotFound = new ArrayList<String>(); protected static File ospCache; protected static boolean zipURLsOK; protected static boolean webConnected; protected static String downloadURL = ""; //$NON-NLS-1$ static { OSP_CACHE_FILTER = new FileFilter() { public boolean accept(File file) { return file.isDirectory() && file.getName().startsWith("osp-"); //$NON-NLS-1$ } }; Runnable runner = new Runnable() { public void run() { webConnected = ResourceLoader.isURLAvailable("http://www.opensourcephysics.org"); //$NON-NLS-1$ } }; new Thread(runner).start(); } /** * Private constructor to prevent instantiation. */ private ResourceLoader() { /** empty block */ } /** * Gets a resource specified by name. If no resource is found using the name * alone, the searchPaths are searched. * * @param name the file or URL name * @return the Resource, or null if none found */ public static Resource getResource(String name) { return getResource(name, true); } /** * Gets a resource specified by name. If no resource is found using the name * alone, the searchPaths are searched. This will find a zip file as a URL * resource, unlike the getResource(String) method. * * @param name the file or URL name * @return the Resource, or null if none found */ public static Resource getResourceZipURLsOK(String name) { zipURLsOK = true; Resource res = getResource(name, true); zipURLsOK = false; return res; } /** * Gets a resource specified by name. If no resource is found using * the name alone, the searchPaths are searched. * Files are searched only if searchFile is true. * * @param name the file or URL name * @param searchFiles true to search files * @return the Resource, or null if none found */ public static Resource getResource(String name, boolean searchFiles) { try { URL url = getAppletResourceURL(name); // added by W. Christian if(url!=null) { return new Resource(url); } } catch(Exception ex) {} return getResource(name, Resource.class, searchFiles); } /** * Gets a resource specified by name and Class. If no resource is found using * the name alone, the searchPaths are searched. * * @param name the file or URL name * @param type the Class providing default ClassLoader resource loading * @return the Resource, or null if none found */ public static Resource getResource(String name, Class<?> type) { return getResource(name, type, true); } /** * Gets a resource specified by name and Class. If no resource is found using * the name alone, the searchPaths are searched. * Files are searched only if searchFile is true. * * @param name the file or URL name * @param type the Class providing default ClassLoader resource loading * @param searchFiles true to search files * @return the Resource, or null if none found */ public static Resource getResource(String name, Class<?> type, boolean searchFiles) { if((name==null)||name.equals("")) { //$NON-NLS-1$ return null; } pathsNotFound.clear(); // Remove leading and trailing inverted commas (added by Paco) if(name.startsWith("\"")) { //$NON-NLS-1$ name = name.substring(1); } if(name.endsWith("\"")) { //$NON-NLS-1$ name = name.substring(0, name.length()-1); } if(name.startsWith("./")) { //$NON-NLS-1$ name = name.substring(2); } if(OSPRuntime.isAppletMode()||(OSPRuntime.applet!=null)) { // added by Paco Resource appletRes = null; // following code added by Doug Brown 2009/11/14 if(type==OSPRuntime.applet.getClass()) { try { URL url = type.getResource(name); appletRes = createResource(url); if(appletRes!=null) { return appletRes; } } catch(Exception ex) {} } // end code added by Doug Brown 2009/11/14 for(Iterator<String> it = searchPaths.iterator(); it.hasNext(); ) { String path = getPath(it.next(), name); appletRes = findResourceInClass(path, type, searchFiles); if(appletRes!=null) { return appletRes; } } appletRes = findResourceInClass(name, type, searchFiles); if(appletRes!=null) { return appletRes; } } // look for resource with name only Resource res = findResource(name, type, searchFiles); if(res!=null) { return res; } pathsNotFound.add(name); StringBuffer err = new StringBuffer("Not found: "+name); //$NON-NLS-1$ err.append(" [searched "+name); //$NON-NLS-1$ // look for resource in searchPaths for(String next: searchPaths) { String path = getPath(next, name); if (pathsNotFound.contains(path)) continue; res = findResource(path, type, searchFiles); if(res!=null) { return res; } pathsNotFound.add(path); err.append(";"+path); //$NON-NLS-1$ } err.append("]"); //$NON-NLS-1$ OSPLog.fine(err.toString()); return null; } /** * Gets a resource specified by base path and name. If base path is relative * and no resource is found using the base alone, the searchPaths are * searched. * * @param basePath the base path * @param name the file or URL name * @return the Resource, or null if none found */ public static Resource getResource(String basePath, String name) { return getResource(basePath, name, Resource.class); } /** * Gets a resource specified by base path and name. If base path is relative * and no resource is found using the base alone, the searchPaths are * searched. Files are searched only if searchFile is true. * * @param basePath the base path * @param name the file or URL name * @param searchFiles true to search files * @return the Resource, or null if none found */ public static Resource getResource(String basePath, String name, boolean searchFiles) { return getResource(basePath, name, Resource.class, searchFiles); } /** * Gets a resource specified by base path, name and class. If base path is * relative and no resource is found using the base alone, the searchPaths * are searched. * * @param basePath the base path * @param name the file or URL name * @param type the Class providing ClassLoader resource loading * @return the Resource, or null if none found */ public static Resource getResource(String basePath, String name, Class<Resource> type) { return getResource(basePath, name, type, true); } /** * Gets a resource specified by base path, name and class. If base path is * relative and no resource is found using the base alone, the searchPaths * are searched. Files are searched only if searchFile is true. * * @param basePath the base path * @param name the file or URL name * @param type the Class providing ClassLoader resource loading * @param searchFiles true to search files * @return the Resource, or null if none found */ public static Resource getResource(String basePath, String name, Class<Resource> type, boolean searchFiles) { if(basePath==null) { return getResource(name, type); } if(name.startsWith("./")) { //$NON-NLS-1$ name = name.substring(2); } // look for resource with basePath and name pathsNotFound.clear(); String path = getPath(basePath, name); Resource res = findResource(path, type, searchFiles); if(res!=null) { return res; } // keep looking only if base path is relative if(basePath.startsWith("/")||(basePath.indexOf(":/")>-1)) { //$NON-NLS-1$ //$NON-NLS-2$ return null; } pathsNotFound.add(path); StringBuffer err = new StringBuffer("Not found: "+path); //$NON-NLS-1$ err.append(" [searched "+path); //$NON-NLS-1$ if(OSPRuntime.applet!=null) { // applet mode String docBase = OSPRuntime.applet.getDocumentBase().toExternalForm(); docBase = XML.getDirectoryPath(docBase)+"/"; //$NON-NLS-1$ path = getPath(getPath(docBase, basePath), name); if (!pathsNotFound.contains(path)) { res = findResource(path, type, searchFiles); if(res!=null) { return res; } pathsNotFound.add(path); err.append(";"+path); //$NON-NLS-1$ } String codeBase = OSPRuntime.applet.getCodeBase().toExternalForm(); if(!codeBase.equals(docBase)) { path = getPath(getPath(codeBase, basePath), name); if (!pathsNotFound.contains(path)) { res = findResource(path, type, searchFiles); if(res!=null) { return res; } pathsNotFound.add(path); err.append(";"+path); //$NON-NLS-1$ } } } // look for resource in searchPaths for(Iterator<String> it = searchPaths.iterator(); it.hasNext(); ) { path = getPath(getPath(it.next(), basePath), name); if (pathsNotFound.contains(path)) continue; res = findResource(path, type, searchFiles); if(res!=null) { return res; } pathsNotFound.add(path); err.append(";"+path); //$NON-NLS-1$ } err.append("]"); //$NON-NLS-1$ OSPLog.fine(err.toString()); return null; } /** * Adds a path at the beginning of the searchPaths list. * * @param base the base path to add */ public static void addSearchPath(String base) { if((base==null)||base.equals("")||(maxPaths<1)) { //$NON-NLS-1$ return; } synchronized(searchPaths) { if(searchPaths.contains(base)) { searchPaths.remove(base); } else { OSPLog.fine("Added path: "+base); //$NON-NLS-1$ } searchPaths.add(0, base); while(searchPaths.size()>Math.max(maxPaths, 0)) { base = searchPaths.get(searchPaths.size()-1); OSPLog.fine("Removed path: "+base); //$NON-NLS-1$ searchPaths.remove(base); } } } /** * Removes a path from the searchPaths list. * * @param base the base path to remove */ public static void removeSearchPath(String base) { if((base==null)||base.equals("")) { //$NON-NLS-1$ return; } synchronized(searchPaths) { if(searchPaths.contains(base)) { OSPLog.fine("Removed path: "+base); //$NON-NLS-1$ searchPaths.remove(base); } } } /** * Adds a search path at the beginning of the applet's search path list. * Added by Wolfgang Christian. * * @param base the base path to add */ public static void addAppletSearchPath(String base) { if((base==null)||(maxPaths<1)) { return; } base=base.trim(); if(!base.endsWith("/"))base=base+"/"; //$NON-NLS-1$//$NON-NLS-2$ synchronized(appletSearchPaths) { if(appletSearchPaths.contains(base)) { appletSearchPaths.remove(base); // search path will be added to top of list later } else { OSPLog.fine("Applet search path added: "+base); //$NON-NLS-1$ } appletSearchPaths.add(0, base); while(appletSearchPaths.size()>Math.max(maxPaths, 0)) { base = appletSearchPaths.get(appletSearchPaths.size()-1); OSPLog.fine("Removed path: "+base); //$NON-NLS-1$ appletSearchPaths.remove(base); } } } /** * Removes a path from the applet search path list. * Added by Wolfgang Christian. * * @param base the base path to remove */ public static void removeAppletSearchPath(String base) { if((base==null)||base.equals("")) { //$NON-NLS-1$ return; } synchronized(appletSearchPaths) { if(appletSearchPaths.contains(base)) { OSPLog.fine("Applet search path removed: "+base); //$NON-NLS-1$ appletSearchPaths.remove(base); } } } /** * Sets the cacheEnabled property. * * @param enabled true to enable the cache */ public static void setCacheEnabled(boolean enabled) { cacheEnabled = enabled; } /** * Gets the cacheEnabled property. * * @return true if the cache is enabled */ public static boolean isCacheEnabled() { return cacheEnabled; } /** * Adds an extension to the end of the extractExtensions list. * Files with this extension found inside jars are extracted before loading. * * @param extension the extension to add */ public static void addExtractExtension(String extension) { if((extension==null)||extension.equals("")) { //$NON-NLS-1$ return; } if(!extension.startsWith(".")) { //$NON-NLS-1$ extension = "."+extension; //$NON-NLS-1$ } OSPLog.finest("Added extension: "+extension); //$NON-NLS-1$ synchronized(extractExtensions) { extractExtensions.add(extension); } } /** * Cancels the current operation when true. * * @param cancel true to cancel */ public static void setCanceled(boolean cancel) { canceled = cancel; } /** * Determines if the current operation is canceled. * * @return true if canceled */ public static boolean isCanceled() { return canceled; } // ___________________________ convenience methods _________________________ /** * Opens and returns an input stream. May return null. * * @param path the path * @return the input stream */ public static InputStream openInputStream(String path) { Resource res = getResource(path); return(res==null) ? null : res.openInputStream(); } /** * Opens and returns a reader. May return null. * * @param path the path * @return the reader */ public static Reader openReader(String path) { Resource res = getResource(path); return(res==null) ? null : res.openReader(); } /** * Gets a string. May return null. * * @param path the path * @return the string */ public static String getString(String path) { Resource res = getResource(path); return(res==null) ? null : res.getString(); } /** * Gets an icon. May return null. * * @param path the path * @return the icon */ public static Icon getIcon(String path) { URL url = getAppletResourceURL(path); // added by W. Christian if(url!=null) { return new ImageIcon(url); } Resource res = getResource(path); return(res==null) ? null : res.getIcon(); } /** * Gets an image. May return null. * * @param path the path * @return the image */ public static Image getImage(String path) { URL url = getAppletResourceURL(path); // added by W. Christian if(url!=null) { return new ImageIcon(url).getImage(); } Resource res = getResource(path); return(res==null) ? null : res.getImage(); } /** * Gets a buffered image. May return null. * * @param path the path * @return the image */ public static BufferedImage getBufferedImage(String path) { Resource res = getResource(path); return(res==null) ? null : res.getBufferedImage(); } /** * Gets a buffered image. May return null. * * @param path the path * @param bufferedImageType one of the types defined by the BufferedImage class * @return the image */ public static BufferedImage getBufferedImage(String path, int bufferedImageType) { Resource res = getResource(path); return(res==null) ? null : res.getBufferedImage(bufferedImageType); } /** * Gets an audio clip. May return null. * * @param path the path * @return the audio clip */ public static AudioClip getAudioClip(String path) { Resource res = getResource(path); return(res==null) ? null : res.getAudioClip(); } /** * Sets the directory for cached files. * * @param newCache the desired cache directory */ public static void setOSPCache(File newCache) { if (newCache!=null && !newCache.equals(ospCache)) { // reject new cache if it is a subdirectory of the current cache! if (ospCache!=null && newCache.getAbsolutePath().contains(ospCache.getAbsolutePath())) { Toolkit.getDefaultToolkit().beep(); return; } if (!newCache.exists() || !newCache.isDirectory()) { if (!newCache.mkdirs()) { Toolkit.getDefaultToolkit().beep(); return; } } if (!newCache.canWrite()) { Toolkit.getDefaultToolkit().beep(); return; } if (ospCache!=null) { // copy existing cached files into new cache and delete old cache File[] hostDirectories = ospCache.listFiles(OSP_CACHE_FILTER); for (File host: hostDirectories) { String hostname = host.getName(); File newHost = new File(newCache, hostname); copyAllFiles(host, newHost); } // copy search cache files into new cache File searchCache = new File(ospCache, SEARCH_CACHE_SUBDIRECTORY); File newSearchCache = new File(newCache, SEARCH_CACHE_SUBDIRECTORY); copyAllFiles(searchCache, newSearchCache); // clear prev cache, including search cache clearOSPCache(ospCache, true); // delete prev cache only if it is empty File[] files = ospCache.listFiles(); if (files!=null && files.length==0) { ospCache.delete(); } } ospCache = newCache; } } /** * Gets the directory for cached files. * * @return the OSP cache */ public static File getOSPCache() { return ospCache; } /** * Gets the default directory for cached files. * * @return the default OSP cache */ public static File getDefaultOSPCache() { String cachePath = System.getProperty("java.io.tmpdir"); //$NON-NLS-1$ String userHome = System.getProperty("user.home"); //$NON-NLS-1$ if (userHome!=null) { userHome += "/"; //$NON-NLS-1$ if (OSPRuntime.isMac()) { cachePath = userHome+ResourceLoader.OSX_DEFAULT_CACHE; } else if (OSPRuntime.isLinux()) { cachePath = userHome+ResourceLoader.LINUX_DEFAULT_CACHE; } else if (OSPRuntime.isWindows()) { String os = System.getProperty("os.name", "").toLowerCase(); //$NON-NLS-1$ //$NON-NLS-2$ if (os.indexOf("xp")>-1) //$NON-NLS-1$ cachePath = userHome+ResourceLoader.WIN_XP_DEFAULT_CACHE; else cachePath = userHome+ResourceLoader.WINDOWS_DEFAULT_CACHE; } } if (cachePath==null) return null; return new File(cachePath); } /** * Uses a JFileChooser to select a cache directory. * @param parent a component to own the file chooser * @return the chosen file */ public static File chooseOSPCache(Component parent) { JFileChooser chooser = new JFileChooser(getOSPCache()); if (OSPRuntime.isMac()) chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); else chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); javax.swing.filechooser.FileFilter folderFilter = new javax.swing.filechooser.FileFilter() { // accept directories only public boolean accept(File f) { if (f==null) return false; return f.isDirectory(); } public String getDescription() { return ToolsRes.getString("LibraryTreePanel.FolderFileFilter.Description"); //$NON-NLS-1$ } }; chooser.setAcceptAllFileFilterUsed(false); chooser.addChoosableFileFilter(folderFilter); String text = ToolsRes.getString("ResourceLoader.FileChooser.Cache"); //$NON-NLS-1$ chooser.setDialogTitle(text); FontSizer.setFonts(chooser, FontSizer.getLevel()); int result = chooser.showDialog(parent, text); if (result==JFileChooser.APPROVE_OPTION) { return chooser.getSelectedFile(); } return null; } /** * Determines if a path defines a file in the OSP cache. * * @param path the path * @return true if path is in the OSP cache */ public static boolean isOSPCachePath(String path) { File cacheFile = getOSPCache(); if (cacheFile==null) cacheFile = new File(System.getProperty("java.io.tmpdir")); //$NON-NLS-1$ String cachePath = XML.forwardSlash(cacheFile.getAbsolutePath())+"/osp-"; //$NON-NLS-1$ if (XML.forwardSlash(path).contains(cachePath)) return true; return false; } /** * Gets the cache file associated with a URL path. * * @param urlPath the path to the file * @return the cache file */ public static File getOSPCacheFile(String urlPath) { return getOSPCacheFile(urlPath, null); } /** * Gets the cache file associated with a URL path. * * @param urlPath the path to the file * @param name name of the file (may be null) * @return the cache file */ public static File getOSPCacheFile(String urlPath, String name) { File cacheFile = getOSPCache(); if (cacheFile==null) cacheFile = new File(System.getProperty("java.io.tmpdir")); //$NON-NLS-1$ return getCacheFile(cacheFile, urlPath, name); } /** * Gets the search cache directory. * * @return the search cache */ public static File getSearchCache() { File ospCache = getOSPCache(); if (ospCache==null) { ospCache = getDefaultOSPCache(); } if (ospCache==null) return null; File searchCache = new File(ospCache, SEARCH_CACHE_SUBDIRECTORY); searchCache.mkdirs(); return searchCache; } /** * Gets the search cache (XML) file associated with a URL path. * * @param urlPath the path to the file * @return the search cache file */ public static File getSearchCacheFile(String urlPath) { String filename = XML.getName(urlPath); String basename = XML.stripExtension(filename); String ext = XML.getExtension(filename); if (ext!=null) basename += "_"+ext; //$NON-NLS-1$ // following line needed to clean up long extensions associated with ComPADRE query paths basename = basename.replace('&', '_').replace('?', '_').replace('=', '_'); filename = basename+".xml"; //$NON-NLS-1$ return getCacheFile(getSearchCache(), urlPath, filename); } /** * Gets the cache file associated with a base cache directory and URL path. * @param cacheFile the base cache directory * @param urlPath the URL path to the original file * @param name name of the file (may be null) * @return the cache file */ private static File getCacheFile(File cacheFile, String urlPath, String name) { String cachePath = XML.forwardSlash(cacheFile.getAbsolutePath()); urlPath = getURIPath(urlPath); String host = ""; //$NON-NLS-1$ String path = ""; //$NON-NLS-1$ String filename = ""; //$NON-NLS-1$ try { URL url = new URL(urlPath); host = url.getHost().replace('.', '_'); path = getNonURIPath(url.getPath()); int n = path.indexOf(cachePath); if (n>-1) { path = path.substring(n+cachePath.length()); } // if path is local, strip drive letter n = path.lastIndexOf(":"); //$NON-NLS-1$ if (n>-1) { path = path.substring(n+1); } // strip leading slash while (path.startsWith("/")) { //$NON-NLS-1$ path = path.substring(1); } String pathname = XML.getName(path); if (!"".equals(pathname)) { //$NON-NLS-1$ path = XML.getDirectoryPath(path); } path = path.replace('.', '_').replace("!", ""); //$NON-NLS-1$ //$NON-NLS-2$ filename = name==null? pathname: name; } catch (MalformedURLException e) { } if ("".equals(host)) //$NON-NLS-1$ host = "local_machine"; //$NON-NLS-1$ if (!path.startsWith("osp-")) //$NON-NLS-1$ cacheFile = new File(cacheFile, "osp-"+host); //$NON-NLS-1$ if (!"".equals(path)) //$NON-NLS-1$ cacheFile = new File(cacheFile, path); if (!"".equals(filename)) //$NON-NLS-1$ cacheFile = new File(cacheFile, filename); return cacheFile; } /** * Downloads a file from the web to the OSP Cache. * * @param urlPath the path to the file * @param fileName the name to assign the downloaded file * @param alwaysOverwrite true to overwrite an existing file, if any * @return the downloaded file, or null if failed to download */ public static File downloadToOSPCache(String urlPath, String fileName, boolean alwaysOverwrite) { if (fileName==null) return null; File target = getOSPCacheFile(urlPath, fileName); File file = ResourceLoader.download(urlPath, target, alwaysOverwrite); if (file==null && webConnected) { webConnected = ResourceLoader.isURLAvailable("http://www.opensourcephysics.org"); //$NON-NLS-1$ if (!webConnected) { JOptionPane.showMessageDialog(null, ToolsRes.getString("LibraryBrowser.Dialog.ServerUnavailable.Message"), //$NON-NLS-1$ ToolsRes.getString("LibraryBrowser.Dialog.ServerUnavailable.Title"), //$NON-NLS-1$ JOptionPane.WARNING_MESSAGE); } else { JOptionPane.showMessageDialog(null, ToolsRes.getString("ResourceLoader.Dialog.FailedToDownload.Message1") //$NON-NLS-1$ +"\n"+ToolsRes.getString("ResourceLoader.Dialog.FailedToDownload.Message2") //$NON-NLS-1$ //$NON-NLS-2$ +"\n"+ToolsRes.getString("ResourceLoader.Dialog.FailedToDownload.Message3"), //$NON-NLS-1$ //$NON-NLS-2$ ToolsRes.getString("ResourceLoader.Dialog.FailedToDownload.Title"), //$NON-NLS-1$ JOptionPane.ERROR_MESSAGE); } } return file; } /** * Returns the HTML code for a local or web HTML page. * @param path the path to the HTML page * @return the HTML code, or null if not found or not HTML */ public static String getHTMLCode(String path) { Resource res = getResourceZipURLsOK(path); if (res==null) return null; String html = res.getString(); if (html!=null && html.trim().startsWith("<!DOCTYPE html")) //$NON-NLS-1$ return html; return null; } /** * Returns the title, if any, of an HTML page. * @param code the HTML code * @return the title, or null if none defined */ public static String getTitleFromHTMLCode(String code) { if (code==null) return null; String[] parts = code.split("<title>"); //$NON-NLS-1$ if (parts.length>1) { parts = parts[1].split("</title>"); //$NON-NLS-1$ if (parts.length>1) { return parts[0].trim(); } } return null; } /** * Returns the first stylesheet link, if any, in an HTML page. * @param code the HTML code * @return the first stylesheet link found, or null if none */ public static String getStyleSheetFromHTMLCode(String code) { if (code==null) return null; // typical tag is in head: <link rel="stylesheet" type="text/css" href="myFolder/myStyleSheet.css"> String[] parts = code.split("<head>"); //$NON-NLS-1$ if (parts.length>1) { parts = parts[1].split("</head>"); //$NON-NLS-1$ if (parts.length>1) { parts = parts[0].split("<link"); //$NON-NLS-1$ if (parts.length>1) { // skip parts[0] for (int i=1; i<parts.length; i++) { if (parts[i].contains("\"stylesheet\"")) { //$NON-NLS-1$ parts = parts[i].split("href"); //$NON-NLS-1$ if (parts.length>1) { parts = parts[1].split("\""); //$NON-NLS-1$ if (parts.length>1) return parts[1]; } } } } } } return null; } /** * Copies an HTML file with associated images and stylesheet to the OSP cache. * Note this does NOT overwrite cache files--to replace, delete them before calling this method * @param htmlPath the path to the source HTML file * @return the copied File, or null if failed */ public static File copyHTMLToOSPCache(String htmlPath) { if (htmlPath==null) return null; // read html text String htmlCode = null; Resource res = getResourceZipURLsOK(htmlPath); if (res!=null) { // if the resource is a file in the OSP cache, return it if (res.getFile()!=null && isOSPCachePath(htmlPath)) { return res.getFile(); } htmlCode = res.getString(); } if (htmlCode!=null) { String htmlBasePath = XML.getDirectoryPath(htmlPath); File htmlTarget = getOSPCacheFile(htmlPath); File targetDirectory = htmlTarget.getParentFile(); targetDirectory.mkdirs(); // look for image references File imageDir = new File(targetDirectory, "images"); //$NON-NLS-1$ String img = "<img "; //$NON-NLS-1$ String pre = "src=\""; //$NON-NLS-1$ String post = "\""; //$NON-NLS-1$ String temp = htmlCode; int j = temp.indexOf(img); if (j>-1) imageDir.mkdirs(); while (j>-1) { temp = temp.substring(j+img.length()); j = temp.indexOf(pre); temp = temp.substring(j+pre.length()); j = temp.indexOf(post); if (j>-1) { String next = temp.substring(0, j); // the image path specified in the html itself String path = XML.getResolvedPath(next, htmlBasePath); res = getResourceZipURLsOK(path); if (res!=null) { // copy image and replace image path in html code String filename = XML.getName(next); File imageTarget = download(path, new File(imageDir, filename), false); if (imageTarget!=null) { path = XML.getPathRelativeTo(imageTarget.getAbsolutePath(), targetDirectory.getAbsolutePath()); if (!next.equals(path)) { htmlCode = htmlCode.replace(pre+next+post, pre+path+post); } } } } j = temp.indexOf(img); } // if separate stylesheet is used, copy to cache and replace in HTML code String css = getStyleSheetFromHTMLCode(htmlCode); if (css!=null && !css.startsWith("http:")) { //$NON-NLS-1$ res = getResourceZipURLsOK(XML.getResolvedPath(css, htmlBasePath)); if (res!=null) { String cssName = XML.getName(css); File cssTarget = new File(targetDirectory, cssName); cssTarget = download(res.getAbsolutePath(), cssTarget, false); if (cssTarget!=null && !cssName.equals(css)) { htmlCode = htmlCode.replace(css, cssName); } } } // write modified html text into target file try { FileWriter fout = new FileWriter(htmlTarget); fout.write(htmlCode); fout.close(); return htmlTarget; } catch(Exception ex) { ex.printStackTrace(); } } return null; } /** * Copies a source file to a target file. * If source file is a directory, copies contents of directory, including subdirectories * * @param inFile the source * @param outFile the target * @return true if all files successfully copied */ public static boolean copyAllFiles(File inFile, File outFile) { if (inFile.isDirectory()) { outFile.mkdir(); boolean success = true; for (File in: inFile.listFiles()) { String filename = in.getName(); File out = new File(outFile, filename); success = success && copyAllFiles(in, out); } return success; } byte[] buffer = new byte[16384]; // 2^14 try { InputStream in = new FileInputStream(inFile); OutputStream out = new FileOutputStream(outFile); while (true) { synchronized (buffer) { int amountRead = in.read(buffer); if (amountRead == -1) { break; } out.write(buffer, 0, amountRead); } } in.close(); out.close(); } catch (IOException ex) { return false; } return true; } /** * Clears an OSP cache. Always deletes "osp-host" directories in cache. Deletes search cache if requested. * * @param cache the cache to clear * @param clearSearchCache true to clear the search cache * @return true if successfully cleared */ public static boolean clearOSPCache(File cache, boolean clearSearchCache) { if (cache==null || !cache.canWrite()) return false; boolean success = true; File[] files = cache.listFiles(OSP_CACHE_FILTER); if (files==null) return true; for(File next: files) { success = success && deleteFile(next); } if (clearSearchCache) { File searchCache = new File(cache, SEARCH_CACHE_SUBDIRECTORY); success = success && deleteFile(searchCache); } if (!success) { JOptionPane.showMessageDialog(null, ToolsRes.getString("ResourceLoader.Dialog.UnableToClearCache.Message1") //$NON-NLS-1$ +"\n"+ToolsRes.getString("ResourceLoader.Dialog.UnableToClearCache.Message2"), //$NON-NLS-1$ //$NON-NLS-2$ ToolsRes.getString("ResourceLoader.Dialog.UnableToClearCache.Title"), //$NON-NLS-1$ JOptionPane.WARNING_MESSAGE); } return success; } /** * Clears an OSP cache host directory. * * @param hostDir the cache host directory to clear * @return true if successfully cleared */ public static boolean clearOSPCacheHost(File hostDir) { if (hostDir==null || !hostDir.canWrite()) return true; if (!OSP_CACHE_FILTER.accept(hostDir)) return false; boolean success = deleteFile(hostDir); if (!success) { JOptionPane.showMessageDialog(null, ToolsRes.getString("ResourceLoader.Dialog.UnableToClearCache.Message1") //$NON-NLS-1$ +"\n"+ToolsRes.getString("ResourceLoader.Dialog.UnableToClearCache.Message2"), //$NON-NLS-1$ //$NON-NLS-2$ ToolsRes.getString("ResourceLoader.Dialog.UnableToClearCache.Title"), //$NON-NLS-1$ JOptionPane.WARNING_MESSAGE); } return success; } /** * Deletes a file or folder. In case of a folder, deletes all contents * and the folder itself. * * @param file the file to delete * @return true if deleted */ public static boolean deleteFile(File file) { if(file.isDirectory()) { File[] files = file.listFiles(); for(File next: files) { deleteFile(next); } } return file.delete(); } /** * Gets the list of files in a directory and its subdirectories that are accepted by a FileFilter. * * @param directory the directory to search * @param filter the FileFilter * @return the list of files */ public static List<File> getFiles(File directory, FileFilter filter) { List<File> results = new ArrayList<File>(); if (directory.isFile()) { results.add(directory); return results; } File[] contents = directory.listFiles(filter); for (File file: contents) { if (file.isDirectory()) { List<File> deeperList = getFiles(file, filter); results.addAll(deeperList); } else { results.add(file); } } return results; } /** * Gets the contents of a zip file. * * @param zipPath the path to the zip file * @return a set of file names in alphabetical order */ public static Set<String> getZipContents(String zipPath) { Set<String> fileNames = new TreeSet<String>(); try { URL url = new URL(getURIPath(zipPath)); OSPLog.finest("zip url: "+url.toExternalForm()); //$NON-NLS-1$ BufferedInputStream bufIn = new BufferedInputStream(url.openStream()); ZipInputStream input = new ZipInputStream(bufIn); ZipEntry zipEntry=null; while ((zipEntry=input.getNextEntry())!=null) { OSPLog.finest("zip entry: "+zipEntry); //$NON-NLS-1$ if (zipEntry.isDirectory()) continue; String fileName = zipEntry.getName(); fileNames.add(fileName); } input.close(); } catch (Exception ex) {} return fileNames; } /** * Unzips a ZIP file into the given directory. ZIP file may be on a server. * Can be canceled using the static setCanceled(boolean) method. * Note this does not warn of possible overwrites. * * @param zipPath the (url) path to the zip file * @param targetDir target directory to save the extracted files * @param alwaysOverwrite true to overwrite existing files, if any * @return the Set of extracted files */ public static Set<File> unzip(String zipPath, File targetDir, boolean alwaysOverwrite) { if (targetDir==null) targetDir = new File(System.getProperty("java.io.tmpdir")); //$NON-NLS-1$ OSPLog.finer("unzipping "+zipPath+" to "+targetDir); //$NON-NLS-1$ //$NON-NLS-2$ try { URL url = new URL(getURIPath(zipPath)); BufferedInputStream bufIn = new BufferedInputStream(url.openStream()); ZipInputStream input = new ZipInputStream(bufIn); ZipEntry zipEntry=null; Set<File> fileSet = new HashSet<File>(); byte[] buffer = new byte[1024]; setCanceled(false); while ((zipEntry=input.getNextEntry()) != null) { if (zipEntry.isDirectory()) continue; if (isCanceled()) { input.close(); return null; } String filename = zipEntry.getName(); File file = new File(targetDir, filename); if (!alwaysOverwrite && file.exists()) { fileSet.add(file); continue; } file.getParentFile().mkdirs(); int bytesRead; FileOutputStream output = new FileOutputStream(file); while ((bytesRead=input.read(buffer)) != -1) output.write(buffer, 0, bytesRead); output.close(); input.closeEntry(); fileSet.add(file); } input.close(); return fileSet; } catch (Exception ex) { ex.printStackTrace(); return null; } } /** * Downloads a file from the web to a target File. * * @param urlPath the path to the file * @param target the target file * @param alwaysOverwrite true to overwrite an existing file, if any * @return the downloaded file, or null if failed */ public static File download(String urlPath, File target, boolean alwaysOverwrite) { // compare urlPath with previous attempt and, if identical, check web connection if (!webConnected || downloadURL.equals(urlPath)) { webConnected = ResourceLoader.isURLAvailable("http://www.opensourcephysics.org"); //$NON-NLS-1$ } if (!webConnected) { JOptionPane.showMessageDialog(null, ToolsRes.getString("LibraryBrowser.Dialog.ServerUnavailable.Message"), //$NON-NLS-1$ ToolsRes.getString("LibraryBrowser.Dialog.ServerUnavailable.Title"), //$NON-NLS-1$ JOptionPane.WARNING_MESSAGE); return null; } urlPath = getURIPath(urlPath); target.getParentFile().mkdirs(); if (alwaysOverwrite || !target.exists()) { OSPLog.finer("downloading "+urlPath+" to "+target); //$NON-NLS-1$ //$NON-NLS-2$ downloadURL = urlPath; try { Resource res = getResourceZipURLsOK(urlPath); InputStream reader = res.openInputStream(); FileOutputStream writer = new FileOutputStream(target); byte[] buffer = new byte[65536]; // 2^14 = 64K int bytesRead = 0; while ((bytesRead = reader.read(buffer)) > 0) { writer.write(buffer, 0, bytesRead); } writer.close(); reader.close(); } catch (Exception ex) { } downloadURL = ""; //$NON-NLS-1$ } if (target.exists()) { return target; } return null; } /** * Extracts a file from a ZIP archive to a target file. ZIP archive may be on a server. * * @param source the path of the file to be extracted (eg "http:/www.server/folder/images.zip!/image1.png") * @param target target file to save * @param alwaysOverwrite true to overwrite existing files, if any * @return the extracted file */ public static File extractFileFromZIP(String source, File target, boolean alwaysOverwrite) { if (!alwaysOverwrite && target.exists()) return target; String lcSource = XML.forwardSlash(source).toLowerCase(); int n = lcSource.indexOf(".zip!/"); //$NON-NLS-1$ if (n==-1) n = lcSource.indexOf(".jar!/"); //$NON-NLS-1$ if (n==-1) n = lcSource.indexOf(".trz!/"); //$NON-NLS-1$ if (n==-1) return null; String sourceName = source.substring(n+6); source = source.substring(0, n+4); try { URL url = new URL(source); BufferedInputStream bufIn = new BufferedInputStream(url.openStream()); ZipInputStream input = new ZipInputStream(bufIn); ZipEntry zipEntry=null; byte[] buffer = new byte[1024]; while ((zipEntry=input.getNextEntry()) != null) { if (zipEntry.isDirectory()) continue; String filename = zipEntry.getName(); if (!sourceName.contains(filename)) continue; target.getParentFile().mkdirs(); int bytesRead; FileOutputStream output = new FileOutputStream(target); while ((bytesRead=input.read(buffer)) != -1) output.write(buffer, 0, bytesRead); output.close(); input.closeEntry(); } input.close(); } catch (Exception ex) { ex.printStackTrace(); return null; } return target; } /** * Determines if a url path is available (ie both valid and connected). * * @param urlPath the path in URI form * @return true if available */ public static boolean isURLAvailable(String urlPath) { try { // make a URL, open a connection, get content URL url = new URL(urlPath); HttpURLConnection urlConnect = (HttpURLConnection)url.openConnection(); urlConnect.getContent(); } catch (Exception ex) { return false; } return true; } /** * Removes protocol and "%20" from URI paths. * * @param uriPath the path in URI form * @return the path */ public static String getNonURIPath(String uriPath) { if (uriPath==null) return null; String path = uriPath; // String path = XML.forwardSlash(uriPath.trim()); boolean isJarOrFile = false; // remove jar protocol, if any if (path.startsWith("jar:")) { //$NON-NLS-1$ path = path.substring(4); isJarOrFile = true; } // remove file protocol, if any if (path.startsWith("file:")) { //$NON-NLS-1$ path = path.substring(5); isJarOrFile = true; } // remove all but one leading slash // commented out by DB 2016-07-08 to enable opening local network files // if (isJarOrFile) { // while (path.startsWith("//")) { //$NON-NLS-1$ // path = path.substring(1); // } // } // remove last leading slash if drive is specified if (path.startsWith("/") && path.indexOf(":")>-1) { //$NON-NLS-1$ //$NON-NLS-2$ path = path.substring(1); } // replace "%20" with space int j = path.indexOf("%20"); //$NON-NLS-1$ while(j>-1) { String s = path.substring(0, j); path = s+" "+path.substring(j+3); //$NON-NLS-1$ j = path.indexOf("%20"); //$NON-NLS-1$ } // // replace "%26" with "&" // j = path.indexOf("%26"); //$NON-NLS-1$ // while(j>-1) { // String s = path.substring(0, j); // path = s+"&"+path.substring(j+3); //$NON-NLS-1$ // j = path.indexOf("%26"); //$NON-NLS-1$ // } return path; } /** * Converts a path to URI form (spaces replaced by "%20", etc). * * @param path the path * @return the path in URI form */ public static String getURIPath(String path) { if (path==null) return null; // trim and change backslashes to forward slashes path = XML.forwardSlash(path.trim()); // add forward slash at end if needed if (!path.equals("") //$NON-NLS-1$ && XML.getExtension(path)==null && !path.endsWith("/")) //$NON-NLS-1$ path += "/"; //$NON-NLS-1$ // replace spaces with "%20" int i = path.indexOf(" "); //$NON-NLS-1$ while(i>-1) { String s = path.substring(0, i); path = s+"%20"+path.substring(i+1); //$NON-NLS-1$ i = path.indexOf(" "); //$NON-NLS-1$ } // // replace "&" with "%26" // i = path.indexOf("&"); //$NON-NLS-1$ // while(i>-1) { // String s = path.substring(0, i); // path = s+"%26"+path.substring(i+1); //$NON-NLS-1$ // i = path.indexOf("&"); //$NON-NLS-1$ // } // add file protocol if path to local file if (!path.equals("") //$NON-NLS-1$ && !path.startsWith("http:") //$NON-NLS-1$ && !path.startsWith("jar:") //$NON-NLS-1$ && !path.startsWith("file:/")) { //$NON-NLS-1$ String protocol = OSPRuntime.isWindows()? "file:/": "file://"; //$NON-NLS-1$ //$NON-NLS-2$ path = protocol+path; } return path; } // ______________________________ private methods ___________________________ /** * Gets the resource URL using the applet's class loader. * Added by Wolfgang Christian. * * @param name of the resource * @return URL of the Resource, or null if none found */ private static URL getAppletResourceURL(String name) { if((OSPRuntime.applet==null)||(name==null)||name.trim().equals("")) { //$NON-NLS-1$ return null; } if(name.startsWith("http:")||name.startsWith("https:")){ //$NON-NLS-1$ //$NON-NLS-2$ // open a direct connection for http and https resources try { return new java.net.URL(name); } catch (MalformedURLException e) { //e.printStackTrace(); } } name=name.trim(); // remove whitespace if(!name.startsWith("/")) { //$NON-NLS-1$ // try applet search paths for relative paths for(Iterator<String> it = appletSearchPaths.iterator(); it.hasNext(); ) { String path = it.next(); String tempName=name; // tempName may change if(tempName.startsWith("../")) { //$NON-NLS-1$ tempName = tempName.substring(3); //remove prefix path=path.substring(0, path.length()-1); // drop trailing slash int last=path.lastIndexOf("/"); //$NON-NLS-1$ // find last directory slash path=(last>0)?path.substring(0, last):"/"; //$NON-NLS-1$ // drop last directory if it exists }else if(tempName.startsWith("./")) { //$NON-NLS-1$ tempName = tempName.substring(2); //remove reference to current directory } URL url = OSPRuntime.applet.getClass().getResource(path+tempName); if(url!=null) { return url; } } } return OSPRuntime.applet.getClass().getResource(name); // url not found in applet search paths } /** * Creates a Resource from a file. * * @param path the file path * @return the resource, if any */ static private Resource createFileResource(String path) { // don't create file resources when in applet mode if(OSPRuntime.applet!=null) { return null; } // ignore paths that refer to zip or jar files if(path.indexOf(".zip")>-1 || path.indexOf(".jar")>-1 || path.indexOf(".trz")>-1) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ return null; } File file = new File(path); try { if(file.exists()&&file.canRead()) { Resource res = new Resource(file); if(path.endsWith("xset")) { //$NON-NLS-1$ xsetZipLoader = null; } OSPLog.finer("File: "+XML.forwardSlash(res.getAbsolutePath())); //$NON-NLS-1$ return res; } } catch(Exception ex) { /** empty block */ } return null; } /** * Creates a Resource from a URL. * * @param path the url path * @return the resource, if any */ static private Resource createURLResource(String path) { // ignore paths that refer to zip or jar files unless explicitly OK if(!zipURLsOK && (path.indexOf(".zip")>-1 || path.indexOf(".jar")>-1 || path.indexOf(".trz")>-1)) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ return null; } Resource res = null; // following added by Doug Brown 2009/11/14 if(OSPRuntime.applet!=null) { try { // let applet class try to get it first //URL url = OSPRuntime.applet.getClass().getResource(path); URL url = getAppletResourceURL(path); res = createResource(url); } catch(Exception ex) { /** empty block */ } } // end code added by Doug Brown 2009/11/14 if(res==null) { // if path includes protocol, use it directly if(path.indexOf(":/")>-1) { //$NON-NLS-1$ try { // URL url = new URL(path); // changed to use URI path 2011/09/11 DB URL url = new URL(getURIPath(path)); res = createResource(url); } catch(Exception ex) { /** empty block */ } } // else if applet mode and relative path, search document and code base else { if((OSPRuntime.applet!=null)&&!path.startsWith("/")) { //$NON-NLS-1$ // first check document base URL docBase = OSPRuntime.applet.getDocumentBase(); try { // following added by Doug Brown 2009/11/14 String basePath = docBase.toString(); // strip query, if any, from document base int n = basePath.indexOf("?"); //$NON-NLS-1$ if(n>-1) { docBase = new URL(basePath.substring(0, n)); } // end code added by Doug Brown 2009/11/14 URL url = new URL(docBase, path); res = createResource(url); } catch(Exception ex) { /** empty block */ } if(res==null) { URL codeBase = OSPRuntime.applet.getCodeBase(); String s = XML.getDirectoryPath(docBase.toExternalForm())+"/"; //$NON-NLS-1$ if(!codeBase.toExternalForm().equals(s)) { try { URL url = new URL(codeBase, path); res = createResource(url); } catch(Exception ex) { /** empty block */ } } } } } } if(res!=null) { if(path.endsWith(".xset")) { //$NON-NLS-1$ xsetZipLoader = null; } OSPLog.finer("URL: "+XML.forwardSlash(res.getAbsolutePath())); //$NON-NLS-1$ } return res; } /** * Creates a Resource from within a zip or jar file. * * @param path the file path * @return the resource, if any */ // @SuppressWarnings("resource") // Java 7 static private Resource createZipResource(String path) { // convert to non-URI form path = getNonURIPath(path); // get separate zip base and relative file name String base = null; String fileName = path; // look for zip or jar base path int i = path.indexOf("zip!/"); //$NON-NLS-1$ if(i==-1) { i = path.indexOf("jar!/"); //$NON-NLS-1$ } if(i==-1) { i = path.indexOf("exe!/"); //$NON-NLS-1$ } if(i==-1) { i = path.indexOf("trz!/"); //$NON-NLS-1$ } if(i>-1) { base = path.substring(0, i+3); fileName = path.substring(i+5); } if(base==null) { if (path.endsWith(".zip") //$NON-NLS-1$ || path.endsWith(".trz") //$NON-NLS-1$ || path.endsWith(".jar") //$NON-NLS-1$ || path.endsWith(".exe")) { //$NON-NLS-1$ String name = XML.stripExtension(XML.getName(path)); base = path; fileName = name+".xset"; //$NON-NLS-1$ } else if (path.endsWith(".xset")) { //$NON-NLS-1$ base = path.substring(0, path.length()-4)+"zip"; //$NON-NLS-1$ } } // if loading from a web file, download to OSP cache boolean isZip = base!=null && (base.endsWith(".zip") || base.endsWith(".jar") || base.endsWith(".trz")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ boolean deleteOnExit = ospCache==null; if (isZip && path.startsWith("http:")) { //$NON-NLS-1$ String zipFileName = XML.getName(base); File zipFile = downloadToOSPCache(base, zipFileName, false); if (zipFile!=null) { if (deleteOnExit) zipFile.deleteOnExit(); base = zipFile.getAbsolutePath(); path = base+"!/"+fileName; //$NON-NLS-1$ } // // // File zipDir = null; // String zipName = XML.getName(base); // if (ospCache!=null) { // try { // URL url = new URL(getURIPath(path)); // String host = url.getHost().replace('.', '_')+"/"; //$NON-NLS-1$ // host += zipName.replace('.', '_') + "/"; //$NON-NLS-1$ // zipDir = new File(ospCache, host); // } catch (MalformedURLException e) {} // } // if (zipDir==null) { // zipDir = new File(System.getProperty("java.io.tmpdir")); //$NON-NLS-1$ // deleteOnExit = true; // } // File zipFile = download(base, zipName, zipDir, false); // if (zipFile!=null) { // if (deleteOnExit) // zipFile.deleteOnExit(); // base = zipFile.getAbsolutePath(); // path = base+"!/"+fileName; //$NON-NLS-1$ // } } URLClassLoader zipLoader = null; URL url = null; if (base!=null) { // following ZipFile code added by D Brown 12 Sep 2013 // look through ZipFile entries for requested fileName try { ZipFile zipFile = new ZipFile(base); Enumeration<? extends ZipEntry> entries = zipFile.entries(); while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); if (entry.getName().equals(fileName) && entry.getSize()>0) { url = new URL("file", null, path); //$NON-NLS-1$ // URL constructor takes "jar" for any ZIP-based file (per Wikipedia) url = new URL("jar", null, url.toExternalForm()); //$NON-NLS-1$ } } zipFile.close(); } catch (IOException ex) { } // end code added 12 Sep 2013 if (url==null) { // use existing zip loader, if any zipLoader = zipLoaders.get(base); if(zipLoader!=null) { url = zipLoader.findResource(fileName); } else { try { // create new zip loader URL[] urls = new URL[] {new URL("file", null, base)}; //$NON-NLS-1$ zipLoader = new URLClassLoader(urls); url = zipLoader.findResource(fileName); if(url==null) { // workaround works in IE? URL classURL = Resource.class.getResource("/"+base); //$NON-NLS-1$ if(classURL!=null) { urls = new URL[] {classURL}; zipLoader = new URLClassLoader(urls); url = zipLoader.findResource(fileName); // zipLoader.close(); // Java 7 } } if(url!=null) { zipLoaders.put(base, zipLoader); } } catch(Exception ex) { /** empty block */ } } } } // if not found, use xset zip loader, if any if((url==null)&&(xsetZipLoader!=null)) { url = xsetZipLoader.findResource(fileName); if(url!=null) { Iterator<String> it = zipLoaders.keySet().iterator(); while(it.hasNext()) { Object key = it.next(); if(zipLoaders.get(key)==xsetZipLoader) { base = (String) key; break; } } } } String launchJarPath = OSPRuntime.getLaunchJarPath(); // if still not found, use launch jar loader, if any if((url==null)&&(launchJarPath!=null)) { zipLoader = zipLoaders.get(launchJarPath); if(zipLoader!=null) { url = zipLoader.findResource(fileName); } else { try { // create new zip loader URL[] urls = new URL[] {new URL("file", null, launchJarPath)}; //$NON-NLS-1$ zipLoader = new URLClassLoader(urls); url = zipLoader.findResource(fileName); if(url==null) { // workaround works in IE? URL classURL = Resource.class.getResource("/"+launchJarPath); //$NON-NLS-1$ if(classURL!=null) { urls = new URL[] {classURL}; zipLoader = new URLClassLoader(urls); url = zipLoader.findResource(fileName); } } if(url!=null) { zipLoaders.put(launchJarPath, zipLoader); } } catch(Exception ex) { /** empty block */ } } if(url!=null) { base = launchJarPath; } } if (url!=null) { // successfully found url // extract file if extension is flagged for extraction Iterator<String> it = extractExtensions.iterator(); while(it.hasNext()) { String ext = it.next(); if(url.getFile().endsWith(ext)) { File zip = new File(base); String targetPath = fileName; String parent = zip.getParent(); // if target path is relative, resolve wrt parent folder of zip file if(parent!=null && !targetPath.startsWith("/") //$NON-NLS-1$ && fileName.indexOf(":/")==-1) { //$NON-NLS-1$ targetPath = XML.getResolvedPath(fileName, parent); } File target = new File(targetPath); if(!target.exists()) { target = JarTool.extract(zip, fileName, targetPath); if (deleteOnExit) target.deleteOnExit(); } return createFileResource(target.getAbsolutePath()); } } try { Resource res = createResource(url); if((res==null)||(res.getAbsolutePath().indexOf(path)==-1)) { return null; } if(fileName.endsWith("xset")) { //$NON-NLS-1$ xsetZipLoader = zipLoader; } OSPLog.finer("Zip: "+XML.forwardSlash(res.getAbsolutePath())); //$NON-NLS-1$ return res; } catch(IOException ex) { /** empty block */ } } return null; } /** * Creates a Resource from a class resource, typically in a jar file. * * @param name the resource name * @param type the class providing the classloader * @return the resource, if any */ static private Resource createClassResource(String name, Class<?> type) { // ignore any name that has a protocol if(name.indexOf(":/")!=-1) { //$NON-NLS-1$ return null; } String originalName = name; int i = name.indexOf("jar!/"); //$NON-NLS-1$ if(i==-1) { i = name.indexOf("exe!/"); //$NON-NLS-1$ } if(i!=-1) { name = name.substring(i+5); } Resource res = null; try { // check relative to root of jarfile containing specified class URL url = type.getResource("/"+name); //$NON-NLS-1$ res = createResource(url); } catch(Exception ex) { /** empty block */ } if(res==null) { try { // check relative to specified class URL url = type.getResource(name); res = createResource(url); } catch(Exception ex) { /** empty block */ } } // if resource is found, log and set launchJarName if not yet set if(res!=null) { String path = XML.forwardSlash(res.getAbsolutePath()); // don't return resources from Java runtime system jars if((path.indexOf("/jre")>-1)&&(path.indexOf("/lib")>-1)) { //$NON-NLS-1$ //$NON-NLS-2$ return null; } // don't return resources that don't contain original name if(!getNonURIPath(path).contains(originalName) && !path.contains(originalName)) { return null; } if(name.endsWith("xset")) { //$NON-NLS-1$ xsetZipLoader = null; } OSPLog.finer("Class resource: "+path); //$NON-NLS-1$ OSPRuntime.setLaunchJarPath(path); } return res; // may be null } /** * Creates a Resource. * * @param url the URL * @return the resource, if any * @throws IOException */ static private Resource createResource(URL url) throws IOException { if(url==null) { return null; } String path = url.toExternalForm(); URL working = url; String content = null; // web-based zip files require special handling int n = path.toLowerCase().indexOf(".zip!/"); //$NON-NLS-1$ if (n==-1) n = path.toLowerCase().indexOf(".jar!/"); //$NON-NLS-1$ if (n==-1) n = path.toLowerCase().indexOf(".trz!/"); //$NON-NLS-1$ if (n>-1 && path.startsWith("http")) { //$NON-NLS-1$ content = path.substring(n+6); working = new URL(path.substring(0, n+4)); } // check that url is accessible InputStream stream = working.openStream(); if(stream.read()==-1) { stream.close(); return null; } stream.close(); Resource res = new Resource(working, content); return res; } /** * Finds the resource using only the class resource loader */ private static Resource findResourceInClass(String path, Class<?> type, boolean searchFiles) { // added by Paco path = path.replaceAll("/\\./", "/"); // This eliminates any embedded /./ //$NON-NLS-1$ //$NON-NLS-2$ if(type==null) { type = Resource.class; } Resource res = null; // look for cached resource if(cacheEnabled) { res = resources.get(path); if((res!=null)&&(searchFiles||(res.getFile()==null))) { OSPLog.finest("Found in cache: "+path); //$NON-NLS-1$ return res; } } if((res = createClassResource(path, type))!=null) { if(cacheEnabled) { resources.put(path, res); } return res; } return null; } private static Resource findResource(String path, Class<?> type, boolean searchFiles) { path = path.replaceAll("/\\./", "/"); // This eliminates any embedded /./ //$NON-NLS-1$ //$NON-NLS-2$ if(type==null) { type = Resource.class; } Resource res = null; // look for cached resource if(cacheEnabled) { res = resources.get(path); if((res!=null)&&(searchFiles||(res.getFile()==null))) { OSPLog.finest("Found in cache: "+path); //$NON-NLS-1$ return res; } } // try to load resource in file/url/zip/class order // search files only if flagged if((searchFiles&&(res = createFileResource(path))!=null) ||(res = createURLResource(path))!=null ||(res = createZipResource(path))!=null ||(res = createClassResource(path, type))!=null) { if(cacheEnabled) { resources.put(path, res); } return res; } return null; } /** * Gets a path from a base path and file name. * * @param base the base path * @param name the file name * @return the path */ private static String getPath(String base, String name) { if(base==null) { base = ""; //$NON-NLS-1$ } if(base.endsWith(".jar") || base.endsWith(".zip") || base.endsWith(".trz")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ base += "!"; //$NON-NLS-1$ } String path = XML.getResolvedPath(name, base); // correct the path so that it works with Mac if(OSPRuntime.isMac()&&path.startsWith("file:/")&&!path.startsWith("file:///")) { //$NON-NLS-1$ //$NON-NLS-2$ path = path.substring(6); while(path.startsWith("/")) { //$NON-NLS-1$ path = path.substring(1); } path = "file:///"+path; //$NON-NLS-1$ } return path; } } /* * Open Source Physics software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License (GPL) as * published by the Free Software Foundation; either version 2 of the License, * or(at your option) any later version. * * Code that uses any portion of the code in the org.opensourcephysics package * or any subpackage (subdirectory) of this package must must also be be * released under the GNU GPL license. * * This software 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place, * Suite 330, Boston MA 02111-1307 USA or view the license online at * http://www.gnu.org/copyleft/gpl.html * * Copyright (c) 2007 The Open Source Physics project * http://www.opensourcephysics.org */