package com.tyndalehouse.step.core.data; import com.tyndalehouse.step.core.exceptions.StepInternalException; import org.crosswire.common.progress.JobManager; import org.crosswire.common.progress.Progress; import org.crosswire.common.util.IOUtil; import org.crosswire.common.util.NetUtil; import org.crosswire.jsword.JSMsg; import org.crosswire.jsword.book.Book; import org.crosswire.jsword.book.BookDriver; import org.crosswire.jsword.book.install.InstallException; import org.crosswire.jsword.book.sword.ConfigEntry; import org.crosswire.jsword.book.sword.NullBackend; import org.crosswire.jsword.book.sword.SwordBook; import org.crosswire.jsword.book.sword.SwordBookDriver; import org.crosswire.jsword.book.sword.SwordBookMetaData; import org.crosswire.jsword.book.sword.SwordConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; /** * Bases the list of books based on the directory listing of a particular folder, as opposed to a zip file of some kind. * * @author chrisburrell */ public class DirectoryListingInstaller extends DirectoryInstaller { public static String DIRECTORY_LISTING_INSTALLER = "directory-listing-installer"; private static final Logger LOGGER = LoggerFactory.getLogger(DirectoryListingInstaller.class); /** * @param holdingDirectory the directory containing all packages */ public DirectoryListingInstaller(final String installerName, final String holdingDirectory) { super(installerName, holdingDirectory, DIRECTORY_LISTING_INSTALLER); } /* (non-Javadoc) * @see org.crosswire.jsword.book.install.Installer#reloadBookList() */ public void reloadBookList() throws InstallException { // TRANSLATOR: Progress label for downloading one or more files. String jobName = JSMsg.gettext("Downloading files"); Progress job = JobManager.createJob(Progress.RELOAD_BOOK_LIST, jobName, Thread.currentThread()); job.beginJob(jobName); try { loaded = false; loadCachedIndex(); } catch (InstallException ex) { job.cancel(); throw ex; } finally { job.done(); } } /* (non-Javadoc) * @see org.crosswire.jsword.book.install.Installer#reloadBookList() */ @Override public void loadCachedIndex() throws InstallException { //create this file dynamically based on what is present in the holding directory final File[] fileList = new File(super.getPackageDirectory()).listFiles(new FilenameFilter() { @Override public boolean accept(final File dir, final String name) { if (name != null && name.endsWith(".zip")) { return true; } return false; } }); //write a file that contains the extract from all other zip files final BookDriver fake = SwordBookDriver.instance(); for (File bookZipFile : fileList) { try { extraConfFile(fake, bookZipFile); } catch (Exception ex) { LOGGER.error("Failed to read: [{}]", bookZipFile.getAbsoluteFile()); LOGGER.trace(ex.getMessage(), ex); } } loaded = true; } /** * Extracts a single conf file to read its details * * @param zipFile the zip file in question */ private void extraConfFile(BookDriver fakeDriver, File zipFile) { InputStream in = null; ZipInputStream zin = null; try { ConfigEntry.resetStatistics(); in = NetUtil.getInputStream(zipFile.toURI()); zin = new ZipInputStream(in); while (true) { ZipEntry entry = zin.getNextEntry(); if (entry == null) { break; } String internal = entry.getName(); if (internal.endsWith(SwordConstants.EXTENSION_CONF)) { LOGGER.trace("Reading a conf file [{}]", entry.getName()); try { int size = (int) entry.getSize(); ByteArrayOutputStream os = new ByteArrayOutputStream(); byte[] bytes = new byte[1024]; int read; while ((read = zin.read(bytes)) > 0) { os.write(bytes, 0, read); } internal = internal.substring(0, internal.length() - 5); if (internal.startsWith(SwordConstants.DIR_CONF + '/')) { internal = internal.substring(7); } SwordBookMetaData sbmd = new SwordBookMetaData(os.toByteArray(), internal); sbmd.setDriver(fakeDriver); Book book = new SwordBook(sbmd, new NullBackend()); entries.put(book.getName(), book); //assume 1 conf file per zip file return; } catch (Exception ex) { LOGGER.error("Failed to load config for entry: {}", internal, ex); } } } ConfigEntry.dumpStatistics(); } catch (IOException ex) { LOGGER.error("Failed to configuration from [{}]", zipFile.getName()); throw new StepInternalException("Error reading directory of zip files.", ex); } finally { IOUtil.close(zin); IOUtil.close(in); } } }