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