/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package photoSpreadLoaders; import inputOutput.CsvReader; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import photoSpread.PhotoSpread; import photoSpread.PhotoSpreadException.BadUUIDStringError; import photoSpreadObjects.PhotoSpreadImage; import photoSpreadObjects.PhotoSpreadObject; import photoSpreadObjects.PhotoSpreadStringObject; import photoSpreadObjects.PhotoSpreadTextFile; import photoSpreadTable.PhotoSpreadCell; import photoSpreadUtilities.Misc; import photoSpreadUtilities.UUID; import photoSpreadUtilities.UUID.FileHashMethod; /** * * @author skandel */ public class PhotoSpreadFileImporter { final static short SKIP_OPTION = 0; final static short NEW_FOLDER_OPTION = 1; final static short ABORT_OPTION = 2; final static short ACCEPT_FILE_OPTION = 0; final static short KEEP_LOOKING_OPTION = 1; public final static String jpeg = "jpeg"; public final static String jpg = "jpg"; public final static String gif = "gif"; public final static String tiff = "tiff"; public final static String tif = "tif"; public final static String png = "png"; public final static String csv = "csv"; public final static String txt = "txt"; public final static String xml = "xml"; public static PhotoSpreadObject importFile(File f, PhotoSpreadCell cell) throws BadUUIDStringError, FileNotFoundException, IOException { return importFile(f, cell, null); } /** * Creates a photoSpread object from a file on disk. It uses the extension * of file to create appropriate object Default is to create a simple string * object from the file name if extension is not supported. * * @param f * the file to be loaded * @param cell * the cell in which the file is being loaded * @param uuidStr * a string from which a UUID can be constructed * @return the object created from the file * @throws BadUUIDStringError * @throws IOException * @throws FileNotFoundException */ public static PhotoSpreadObject importFile(File f, PhotoSpreadCell cell, String uuidStr) throws BadUUIDStringError, FileNotFoundException, IOException { if (isImage(f)) { if (uuidStr != null) try { return new PhotoSpreadImage(cell, UUID .createFromUUIDString(uuidStr), f.getAbsolutePath()); } catch (BadUUIDStringError e) { return new PhotoSpreadImage(cell, new UUID(f, FileHashMethod.USE_FILE_SAMPLING), f .getAbsolutePath()); } else return new PhotoSpreadImage(cell, f.getAbsolutePath()); } else if (isTextFile(f)) { if (uuidStr != null) try { return new PhotoSpreadTextFile(cell, f.getAbsolutePath(), uuidStr); } catch (BadUUIDStringError e) { return new PhotoSpreadTextFile(cell, f.getAbsolutePath(), new UUID(f, FileHashMethod.USE_WHOLE_FILE)); } else return new PhotoSpreadTextFile(cell, f.getAbsolutePath()); } return new PhotoSpreadStringObject(cell, f.getName()); } public static boolean isImage(File f) { String fileName = f.getName().toLowerCase(); if ((fileName.endsWith(".jpg")) || (fileName.endsWith(".jpeg")) || (fileName.endsWith(".gif")) || (fileName.endsWith(".png"))) { return true; } return false; } public static boolean isTextFile(File f) { String fileName = f.getName(); if (fileName.toLowerCase().endsWith(".txt")) { return true; } return false; } /** * Given the File object for a PhotoSpread metadata csv file, and a cell * object, import all the objects that are described in the csv file into * that cell. If an image's file path is relative, then we take its path to * be relative to the csv file. All the UUIDs are properly associated with * their objects. * * @param f File object for the csv file to be imported. * @param cell The PhotoSpread cell to import into. * @return Number of records read from the CSV file. Note that * this may be much higher than the objects that were * created as part of the import. CSV rows with just * commas (from empty Excel rows) are records, but don't * turn into objects. * @throws BadUUIDStringError */ public static int importCSV(File f, PhotoSpreadCell cell) throws BadUUIDStringError { int numRecordsLoaded = 0; int filePathIndex = -1; int uuidStrIndex = -1; String filename = "<unknown>"; String uuidStr; File imgFile = null; String csvFileDirStr = f.getParent(); boolean ignoreMissingFiles = false; try { CsvReader reader = new CsvReader(f.getPath()); String[] headers; PhotoSpreadObject object; boolean reTrying = false; if (reader.readHeaders()) { headers = reader.getHeaders(); for (int i = 0; i < headers.length; i++) { if (headers[i].equals(PhotoSpreadCell.FILEPATH)) filePathIndex = i; if (headers[i].equals(PhotoSpreadCell.OBJECT_ID)) uuidStrIndex = i; } if (filePathIndex < 0) { return numRecordsLoaded; } while (true) { try { if (!reTrying) { if (!reader.readRecord()) return numRecordsLoaded; // Get file name of image: filename = reader.get(filePathIndex); imgFile = new File(filename); } else reTrying = false; // We always construct a new absolute path // from csvFileDirStr and the file's basename. // This enables users to interactively identify // a directory different from what's in the // CSV file: // Make sure the image gets its original UUID back. // If we know where in the CSV file the stringified // UUID of the image resides, create a UUID from that // string: if (uuidStrIndex > -1) { uuidStr = reader.get(uuidStrIndex).replaceAll("[\'=]", ""); imgFile = resolveFile(csvFileDirStr, imgFile, UUID.createFromUUIDString(uuidStr)); object = PhotoSpreadFileImporter.importFile( imgFile, cell, uuidStr); } else imgFile = resolveFile(csvFileDirStr, imgFile, null); object = PhotoSpreadFileImporter.importFile( imgFile, cell); for (int i = 0; i < headers.length; i++) { if ((i != filePathIndex) && (i != uuidStrIndex)) object.setMetaData(headers[i], reader.get(i)); } cell.addObject(object); numRecordsLoaded++; } catch (java.io.IOException e) { if (ignoreMissingFiles) continue; int userDecision = getFileImportProblemAdvice(imgFile.getAbsolutePath()); switch (userDecision) { case JOptionPane.CLOSED_OPTION: case ABORT_OPTION: return numRecordsLoaded; case SKIP_OPTION: ignoreMissingFiles = true; continue; case NEW_FOLDER_OPTION: File newDir = Misc.getFileNameFromUser( null, // Let user see all files (no file filter) JFileChooser.DIRECTORIES_ONLY); if (newDir == null) // User canceled. return numRecordsLoaded; csvFileDirStr = newDir.getPath(); ignoreMissingFiles = false; reTrying = true; continue; default: continue; } } // end catch clause } // end while } // end if } // end outer try catch (java.io.IOException e) { Misc.showErrorMsg( "During CSV import: " + e.getMessage(), PhotoSpread.getCurrentSheetWindow()); } return numRecordsLoaded; } protected static int getFileImportProblemAdvice(String filePath) { String[] missingFileRemedyOptions = new String[] { "Skip Missing Files", // SKIP_OPTION "Identify New File Folder", // NEW_FOLDER_OPTION "Abort Import", }; // ABORT_OPTION int decision = JOptionPane.showOptionDialog( PhotoSpread.getCurrentSheetWindow(), // Component to show dialog with "File '" + filePath + "' was not found.\n" + "Option '" + missingFileRemedyOptions[NEW_FOLDER_OPTION] + "' lets you pick any file as an alternative\n" + "folder, where the files referenced in the CSV file are now (at least mostly) located.\n" + "Choose one of:", "Missing file reference in CSV file", // title in top of window frame. JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, missingFileRemedyOptions, NEW_FOLDER_OPTION); return decision; } protected static File resolveFile(String startPath, File originalFile, UUID uuid) throws IOException{ if(originalFile.exists()) return originalFile; String path = originalFile.getPath(); String[] parents = path.split(File.separator); StringBuffer relativePath = new StringBuffer(""); for(int i = parents.length-1; i > 0; --i){ relativePath.insert(0, File.separator + parents[i]); File candidate = new File(startPath + relativePath.toString()); if(candidate.exists()){ if(uuid==null){ int decision = getMissingUUIDProblemAdvice(originalFile.getPath(), candidate.getPath()); if(decision == KEEP_LOOKING_OPTION) continue; } else{ UUID candidateUUID = new UUID(candidate, FileHashMethod.USE_FILE_SAMPLING); if(!uuid.equals(candidateUUID)){ int decision = getMissingUUIDProblemAdvice(originalFile.getPath(), candidate.getPath()); if(decision == KEEP_LOOKING_OPTION) continue; } } return candidate; } } throw new java.io.IOException("Could not resolve file path"); } protected static int getMissingUUIDProblemAdvice(String savedFilePath, String candidateFilePath) { String[] incorrectUUIDRemedyOptions = new String[] { "Accept File", // Accept file "Keep Looking", }; // Keep looking int decision = JOptionPane.showOptionDialog( PhotoSpread.getCurrentSheetWindow(), // Component to show dialog with "File '" + savedFilePath + "' was not found. \n But we found this file: " + candidateFilePath + " \n which has a different UUID.\n" + "Option '" + incorrectUUIDRemedyOptions[ACCEPT_FILE_OPTION] + "' accepts this candidate file\n" + "If this is not the right file, select option '" + incorrectUUIDRemedyOptions[KEEP_LOOKING_OPTION] + "' and we will keep loooking", "Mismatched UUID", // title in top of window frame. JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, incorrectUUIDRemedyOptions, NEW_FOLDER_OPTION); return decision; } }