f@0: /* f@0: CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool f@0: f@0: Copyright (C) 2011 Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/) f@0: f@0: This program is free software: you can redistribute it and/or modify f@0: it under the terms of the GNU General Public License as published by f@0: the Free Software Foundation, either version 3 of the License, or f@0: (at your option) any later version. f@0: f@0: This program is distributed in the hope that it will be useful, f@0: but WITHOUT ANY WARRANTY; without even the implied warranty of f@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the f@0: GNU General Public License for more details. f@0: f@0: You should have received a copy of the GNU General Public License f@0: along with this program. If not, see . f@0: */ f@0: package uk.ac.qmul.eecs.ccmi.gui; f@0: f@0: import java.awt.Frame; f@0: import java.io.BufferedInputStream; f@0: import java.io.BufferedOutputStream; f@0: import java.io.File; f@0: import java.io.FileInputStream; f@0: import java.io.FileNotFoundException; f@0: import java.io.FileOutputStream; f@0: import java.io.IOException; f@0: import java.io.InputStream; f@0: import java.io.OutputStream; f@0: import java.util.ResourceBundle; f@0: f@0: import javax.swing.JFileChooser; f@0: import javax.swing.JOptionPane; f@0: f@0: import uk.ac.qmul.eecs.ccmi.gui.filechooser.FileChooser; f@0: import uk.ac.qmul.eecs.ccmi.gui.filechooser.FileChooserFactory; f@0: import uk.ac.qmul.eecs.ccmi.sound.SoundEvent; f@0: import uk.ac.qmul.eecs.ccmi.sound.SoundFactory; f@0: import uk.ac.qmul.eecs.ccmi.utils.PreferencesService; f@0: f@0: /** f@0: * A Utility class providing inner classes and interfaces for handling f@0: * files. f@0: */ f@0: public abstract class FileService f@0: { f@0: f@0: /** f@0: * An Open object encapsulates the stream, name and path of the file that the user selected for opening. f@0: */ f@0: public interface Open f@0: { f@0: /** f@0: * Gets the input stream corresponding to the user selection. f@0: * @return the input stream, or null if the user cancels the file selection task f@0: */ f@0: InputStream getInputStream(); f@0: /** f@0: * Gets the name of the file that the user selected. f@0: * @return the file name, or null if the user cancels the file selection task f@0: */ f@0: String getName(); f@0: f@0: /** f@0: * Gets the path of the file that the user selected. f@0: * @return the file path , or null if the user cancels the file selection task f@0: */ f@0: String getPath(); f@0: f@0: } f@0: f@0: /** f@0: * A Save object encapsulates the stream and name of the file that the user selected for saving. f@0: */ f@0: public interface Save f@0: { f@0: /** f@0: * Gets the output stream corresponding to the user selection. f@0: * @return the output stream, or null if the user cancels the file selection task f@0: */ f@0: OutputStream getOutputStream(); f@0: /** f@0: * Gets the name of the file that the user selected. f@0: * @return the file name, or null if the user cancels the file selection task f@0: */ f@0: String getName(); f@0: /** f@0: * Gets the path of the file that the user selected. f@0: * @return the file path, or null if the user cancels the file selection task f@0: */ f@0: String getPath(); f@0: } f@0: f@0: /** f@0: * This class returns a FileService for opening and saving files, with either a JFileChooser or a f@0: * SpeechFileChooser f@0: */ f@0: public static class ChooserService f@0: { f@0: /** f@0: * Creates a new {@code ChooserService}. f@0: * f@0: * @param initialDirectory the directory displayed when the files service is displayed f@0: */ f@0: public ChooserService(File initialDirectory){ f@0: useAccessible = Boolean.parseBoolean(PreferencesService.getInstance().get("use_accessible_filechooser", "true")); f@0: fileChooser = FileChooserFactory.getFileChooser(useAccessible); f@0: fileChooser.setCurrentDirectory(initialDirectory); f@0: } f@0: f@0: /** f@0: * Returns a {@code FileService.Open} out of a file chosen by the user. The user is prompted f@0: * with either a normal or an accessible file choser. f@0: * f@0: * @param defaultDirectory the directory to open the file chooser, prompted to the user f@0: * @param defaultFile the file selected when the file chooser is prompted to the user f@0: * @param filter an {@code ExtensionFilter} to filter the filed displayed on the file chooser f@0: * @param frame the frame where the file chooser is displayed f@0: * @return an {@code FileService.Open} to handle the file selected by the user f@0: * @throws IOException if the file chosen by the uses doesn't exist. f@0: */ f@0: public FileService.Open open(String defaultDirectory, String defaultFile, f@0: ExtensionFilter filter, Frame frame) throws IOException { f@0: checkChangedOption(); f@0: fileChooser.resetChoosableFileFilters(); f@0: fileChooser.setFileFilter(filter); f@0: if (defaultDirectory != null) f@0: fileChooser.setCurrentDirectory(new File(defaultDirectory)); f@0: if (defaultFile == null) f@0: fileChooser.setSelectedFile(null); f@0: else f@0: fileChooser.setSelectedFile(new File(defaultFile)); f@0: int response = fileChooser.showOpenDialog(frame); f@0: if (response == JFileChooser.APPROVE_OPTION) f@0: return new OpenImpl(fileChooser.getSelectedFile()); f@0: else{ f@0: /* If the user cancels the task (presses cancel button or the X at the top left corner) * f@0: * the CANCEl sound is played (together with the registered playerListeenr if any) */ f@0: if(useAccessible) f@0: SoundFactory.getInstance().play(SoundEvent.CANCEL); f@0: return new OpenImpl(null); f@0: } f@0: } f@0: f@0: /** f@0: * Returns a {@code FileService.Save} out of a file chosen by the user. The user is prompted f@0: * with either a normal or an accessible file chooser. f@0: * f@0: * @param defaultDirectory the directory to open the file chooser, prompted to the user f@0: * @param defaultFile the file selected when the file chooser is prompted to the user f@0: * @param filter an {@code ExtensionFilter} to filter the filed displayed on the file chooser f@0: * @param removeExtension the extension to be removed from the chosen file name. Use {@code null} for removing no extension f@0: * @param addExtension the extension to be added to the chosen file name. f@0: * @param currentTabs an array of already open files names. If the selected file matches any of these, then an f@0: * Exception is thrown. If {@code null} is passed, then this parameter will be ignored and no check will be done. f@0: * @return an {@code FileService.Save} to handle the file selected by the user f@0: * @throws IOException if the file chosen by the uses doesn't exist or has a file name that's already f@0: * in {@code currentTabs}. f@0: */ f@0: public FileService.Save save(String defaultDirectory, String defaultFile, f@0: ExtensionFilter filter, String removeExtension, String addExtension, String[] currentTabs) throws IOException { f@0: checkChangedOption(); f@0: fileChooser.resetChoosableFileFilters(); f@0: fileChooser.setFileFilter(filter); f@0: if (defaultDirectory == null) f@0: fileChooser.setCurrentDirectory(new File(".")); f@0: else f@0: fileChooser.setCurrentDirectory(new File(defaultDirectory)); f@0: if (defaultFile != null){ f@0: File f = new File(editExtension(defaultFile, removeExtension, addExtension)); f@0: if(f.exists()) f@0: fileChooser.setSelectedFile(f); f@0: else f@0: fileChooser.setSelectedFile(null); f@0: }else f@0: fileChooser.setSelectedFile(null); f@0: int response = fileChooser.showSaveDialog(null); f@0: if (response == JFileChooser.APPROVE_OPTION){ f@0: ResourceBundle resources = ResourceBundle.getBundle(EditorFrame.class.getName()); f@0: File f = fileChooser.getSelectedFile(); f@0: if (addExtension != null && f.getName().indexOf(".") < 0) // no extension supplied f@0: f = new File(f.getPath() + addExtension); f@0: f@0: String fileName = getFileNameFromPath(f.getAbsolutePath(),false); f@0: /* check the name against the names of already open tabs */ f@0: if(currentTabs != null){ f@0: for(String tab : currentTabs){ f@0: if(fileName.equals(tab)) f@0: throw new IOException(resources.getString("dialog.error.same_file_name")); f@0: } f@0: } f@0: f@0: if (!f.exists()) // file doesn't exits return the new SaveImpl with no problems f@0: return new SaveImpl(f); f@0: f@0: /* file with this name already exists, we must ask the user to confirm */ f@0: if(useAccessible){ f@0: int result = SpeechOptionPane.showConfirmDialog( f@0: null, f@0: resources.getString("dialog.overwrite"), f@0: SpeechOptionPane.YES_NO_OPTION); f@0: if (result == SpeechOptionPane.YES_OPTION) f@0: return new SaveImpl(f); f@0: }else{ f@0: int result = JOptionPane.showConfirmDialog( f@0: null, f@0: resources.getString("dialog.overwrite"), f@0: null, f@0: JOptionPane.YES_NO_OPTION); f@0: if (result == JOptionPane.YES_OPTION) f@0: return new SaveImpl(f); f@0: } f@0: } f@0: /* If the user cancels the task (presses cancel button or the X at the top left) * f@0: * the CANCEl sound is played (together with the registered playerListeenr if any) */ f@0: if(useAccessible) f@0: SoundFactory.getInstance().play(SoundEvent.CANCEL); f@0: /* returned if the user doesn't want to overwrite the file */ f@0: return new SaveImpl(null); f@0: } f@0: f@0: /* check if the user has changed the configuration since the last time a the fileChooser was shown */ f@0: private void checkChangedOption(){ f@0: boolean useAccessible = Boolean.parseBoolean(PreferencesService.getInstance().get("use_accessible_filechooser", "true")); f@0: if(this.useAccessible != useAccessible){ f@0: this.useAccessible = useAccessible; f@0: File currentDir = fileChooser.getCurrentDirectory(); f@0: fileChooser = FileChooserFactory.getFileChooser(useAccessible); f@0: fileChooser.setCurrentDirectory(currentDir); f@0: } f@0: } f@0: f@0: private FileChooser fileChooser; f@0: private boolean useAccessible; f@0: } f@0: f@0: /** f@0: * A file service which doesn't show any dialog to let f@0: * the user choose which file to open or save. f@0: */ f@0: public static class DirectService { f@0: /** f@0: * Creates a {@code FileService.Open} out of the file passed as argument. f@0: * f@0: * @param file the file to open f@0: * @return a new {@code FileService.Open} instance f@0: * @throws IOException if {@code file} cannot be found f@0: */ f@0: public FileService.Open open(File file) throws IOException{ f@0: return new OpenImpl(file); f@0: } f@0: f@0: /** f@0: * Creates a {@code FileService.Save} out of the file passed as argument. f@0: * f@0: * @param file the file to save f@0: * @return a new {@code FileService.Save} instance f@0: * @throws IOException if {@code file} cannot be found f@0: */ f@0: public FileService.Save save(File file) throws IOException{ f@0: return new SaveImpl(file); f@0: } f@0: } f@0: f@0: private static class SaveImpl implements FileService.Save{ f@0: public SaveImpl(File f) throws FileNotFoundException{ f@0: if (f != null){ f@0: path = f.getPath(); f@0: name = getFileNameFromPath(path,false); f@0: out = new BufferedOutputStream(new FileOutputStream(f)); f@0: } f@0: } f@0: f@0: @Override f@0: public String getName() { return name; } f@0: @Override f@0: public String getPath() {return path; } f@0: @Override f@0: public OutputStream getOutputStream() { return out; } f@0: f@0: private String name; f@0: private String path; f@0: private OutputStream out; f@0: } f@0: f@0: private static class OpenImpl implements FileService.Open{ f@0: public OpenImpl(File f) throws FileNotFoundException{ f@0: if (f != null){ f@0: path = f.getPath(); f@0: name = getFileNameFromPath(path,false); f@0: in = new BufferedInputStream(new FileInputStream(f)); f@0: } f@0: } f@0: f@0: @Override f@0: public String getName() { return name; } f@0: @Override f@0: public String getPath() { return path; } f@0: @Override f@0: public InputStream getInputStream() { return in; } f@0: f@0: private String path; f@0: private String name; f@0: private InputStream in; f@0: } f@0: f@0: /** f@0: * Edits the file path so that it ends in the desired f@0: * extension. f@0: * @param original the file to use as a starting point f@0: * @param toBeRemoved the extension that is to be f@0: * removed before adding the desired extension. Use f@0: * null if nothing needs to be removed. f@0: * @param desired the desired extension (e.g. ".png"), f@0: * or a | separated list of extensions f@0: * @return original if it already has the desired f@0: * extension, or a new file with the edited file path f@0: */ f@0: public static String editExtension(String original, f@0: String toBeRemoved, String desired){ f@0: if (original == null) return null; f@0: int n = desired.indexOf('|'); f@0: if (n >= 0) desired = desired.substring(0, n); f@0: String path = original; f@0: if (!path.toLowerCase().endsWith(desired.toLowerCase())){ f@0: if (toBeRemoved != null && path.toLowerCase().endsWith( f@0: toBeRemoved.toLowerCase())) f@0: path = path.substring(0, path.length() - toBeRemoved.length()); f@0: path = path + desired; f@0: } f@0: return path; f@0: } f@0: f@0: /** f@0: * Returns the single file name from a file path f@0: * f@0: * @param path the path to extract the file name from f@0: * @param keepExtension whether to keep the extension of the file f@0: * in the returned string f@0: * @return the name of the file identified by {@code path} f@0: */ f@0: public static String getFileNameFromPath(String path,boolean keepExtension){ f@0: int index = path.lastIndexOf(System.getProperty("file.separator")); f@0: String name; f@0: if(index == -1) f@0: name = path; f@0: else f@0: name = path.substring(index+1); f@0: if(!keepExtension){ f@0: index = name.lastIndexOf('.'); f@0: if(index != -1) f@0: name = name.substring(0, index); f@0: } f@0: return name; f@0: } f@0: }