Package com.cburch.logisim.file

Source Code of com.cburch.logisim.file.Loader$LogisimFileFilter

/* Copyright (c) 2010, Carl Burch. License information is located in the
* com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */

package com.cburch.logisim.file;

import java.awt.Component;
import java.awt.Dimension;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;

import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.filechooser.FileFilter;

import com.cburch.logisim.std.Builtin;
import com.cburch.logisim.tools.Library;
import com.cburch.logisim.util.JFileChoosers;
import com.cburch.logisim.util.MacCompatibility;
import com.cburch.logisim.util.ZipClassLoader;

import static com.cburch.logisim.util.LocaleString.*;

import org.slf4j.*;

/**
* A class to encapsulate loading functionality
* of files or library objects
*/
public class Loader implements LibraryLoader {
    private static final Logger logger =
        LoggerFactory.getLogger( Loader.class );

    public static final String LOGISIM_EXTENSION = ".circ";
    public static final FileFilter LOGISIM_FILTER = new LogisimFileFilter();
    public static final FileFilter JAR_FILTER = new JarFileFilter();

    private static class LogisimFileFilter extends FileFilter {
        @Override
        public boolean accept(File f) {
            return f.isDirectory()
                || f.getName().endsWith(LOGISIM_EXTENSION);
        }

        @Override
        public String getDescription() {
            return getFromLocale("logisimFileFilter");
        }
    }

    private static class JarFileFilter extends FileFilter {
        @Override
        public boolean accept(File f) {
            return f.isDirectory()
                || f.getName().endsWith(".jar");
        }

        @Override
        public String getDescription() {
            return getFromLocale("jarFileFilter");
        }
    }

    // fixed
    private Component parent;
    private Builtin builtin = new Builtin();

    // to be cleared with each new file
    private File mainFile = null;
    private Stack<File> filesOpening = new Stack<File>();
    private Map<File,File> substitutions = new HashMap<File,File>();

    /**
     * Constructor
     * @param parent the parent
     */
    public Loader(Component parent) {
        this.parent = parent;
        clear();
    }

    /**
     * Get built-in functionality.
     * @return the built-in functionality
     */
    public Builtin getBuiltin() {
        return builtin;
    }

    /**
     * Sets the parent of the component from a high level.
     * @param value the component's new parent
     */
    public void setParent(Component value) {
        parent = value;
    }

    /**
     * Get substitutions from a source file
     * @param source the source file
     * @return the returned file
     */
    private File getSubstitution(File source) {
        File ret = substitutions.get(source);
        return ret == null ? source : ret;
    }

    /**
     * Get the file from the file chooser
     * @return the selected file
     */
    public File getMainFile() {
        return mainFile;
    }

    /**
     * Spawns a file chooser
     * @return the JFileChooser
     */
    public JFileChooser createChooser() {
        return JFileChoosers.createAt(getCurrentDirectory());
    }

    // used here and in LibraryManager only
    File getCurrentDirectory() {
        File ref;
        if (!filesOpening.empty()) {
            ref = filesOpening.peek();
        } else {
            ref = mainFile;
        }
        return ref == null ? null : ref.getParentFile();
    }

    private void setMainFile(File value) {
        mainFile = value;
    }

    /**
     * More substantive methods accessed from outside this package.
     */
    public void clear() {
        filesOpening.clear();
        mainFile = null;
    }

    /**
     * Open a file from an array of files to substitute.
     * @param file the main file
     * @param substitutions the substitution files
     * @return A functioning LogisimFile
     * @throws LoadFailedException if there was an error loading the file
     */
    public LogisimFile openLogisimFile(File file, Map<File,File> substitutions)
            throws LoadFailedException {
        this.substitutions = substitutions;
        // FIXME allevaton: What's up with this? Try finally with no catch?
        try {
            return openLogisimFile(file);
        } finally {
            this.substitutions = Collections.emptyMap();
        }
    }

    /**
     * Open a file from the hard drive.
     * @param file the file object
     * @return the LogisimFile object
     * @throws LoadFailedException if there was a problem loading the file
     */
    public LogisimFile openLogisimFile(File file) throws LoadFailedException {
        try {
            LogisimFile ret = loadLogisimFile(file);
            if (ret != null) {
                setMainFile(file);
            }

            showMessages(ret);
            return ret;
        } catch (LoaderException e) {
            logger.error( "An error occurred when loading the file.\n"
                    + e.getMessage() );
            throw new LoadFailedException(e.getMessage(), e.isShown());
        }
    }

    /**
     * Open a file from an input stream
     * @param reader The input stream
     * @return a fully functioning LogisimFile object
     * @throws LoadFailedException if it failed to load
     * @throws IOException if another error occurred
     */
    public LogisimFile openLogisimFile(InputStream reader)
            throws LoadFailedException, IOException {
        LogisimFile ret = null;
        try {
            ret = LogisimFile.load(reader, this);
        } catch (LoaderException e) {
            logger.error( "An error occurred when loading the file.\n"
                    + e.getMessage() );
            return null;
        }
        showMessages(ret);
        return ret;
    }

    /**
     * Loads a specified Logisim library
     * @param file the library file
     * @return the library object
     */
    public Library loadLogisimLibrary(File file) {
        File actual = getSubstitution(file);
        LoadedLibrary ret = LibraryManager.instance.loadLogisimLibrary(this, actual);
        if (ret != null) {
            LogisimFile retBase = (LogisimFile) ret.getBase();
            showMessages(retBase);
        }
        return ret;
    }

    /**
     * Loads a library as a jar
     * @param file the jar file
     * @param className the class in the jar
     * @return the library object
     */
    public Library loadJarLibrary(File file, String className) {
        File actual = getSubstitution(file);
        return LibraryManager.instance.loadJarLibrary(this, actual, className);
    }

    /**
     * Reload the specified library.
     * @param lib The specified library
     */
    public void reload(LoadedLibrary lib) {
        LibraryManager.instance.reload(this, lib);
    }

    /**
     * Save the current file.
     * @param file The source file
     * @param dest The destination file
     * @return if the safe was a success
     */
    public boolean save(LogisimFile file, File dest) {
        Library reference = LibraryManager.instance.findReference(file, dest);
        if (reference != null) {
            JOptionPane.showMessageDialog(parent,
                    getFromLocale("fileCircularError", reference.getDisplayName()),
                    getFromLocale("fileSaveErrorTitle"),
                    JOptionPane.ERROR_MESSAGE);
            return false;
        }

        File backup = determineBackupName(dest);
        boolean backupCreated = backup != null && dest.renameTo(backup);

        FileOutputStream fwrite = null;
        try {
            try {
                MacCompatibility.setFileCreatorAndType(dest, "LGSM", "circ");
            } catch (IOException e) {
                logger.warn( "Could not set Mac compatible"
                        + " file associative flags (file creator and type)" );
            }
            fwrite = new FileOutputStream(dest);
            file.write(fwrite, this);
            file.setName(toProjectName(dest));

            File oldFile = getMainFile();
            setMainFile(dest);
            LibraryManager.instance.fileSaved(this, dest, oldFile, file);
        } catch (IOException e) {
            if (backupCreated) {
                logger.info( "Backup found, recovering from it." );
                recoverBackup(backup, dest);
            }

            if (dest.exists() && dest.length() == 0) {
                dest.delete();
            }

            JOptionPane.showMessageDialog(parent,
                getFromLocale("fileSaveError",
                    e.toString()),
                getFromLocale("fileSaveErrorTitle"),
                JOptionPane.ERROR_MESSAGE);
            return false;
        }

        if (fwrite != null) {
            try {
                fwrite.close();
            } catch (IOException e) {
                if (backupCreated) {
                    logger.info( "Backup found, recovering from it." );
                    recoverBackup(backup, dest);
                }
                if (dest.exists() && dest.length() == 0) {
                    dest.delete();
                }

                JOptionPane.showMessageDialog(parent,
                    getFromLocale("fileSaveCloseError",
                        e.toString()),
                    getFromLocale("fileSaveErrorTitle"),
                    JOptionPane.ERROR_MESSAGE);
                return false;
            }
        }

        if (!dest.exists() || dest.length() == 0) {
            if (backupCreated && backup != null && backup.exists()) {
                recoverBackup(backup, dest);
            } else {
                dest.delete();
            }
            JOptionPane.showMessageDialog(parent,
                    getFromLocale("fileSaveZeroError"),
                    getFromLocale("fileSaveErrorTitle"),
                    JOptionPane.ERROR_MESSAGE);
            return false;
        }

        if (backupCreated && backup.exists()) {
            backup.delete();
        }
        return true;
    }

    /**
     * Get a name for a backup file
     * @param base the base file
     * @return the backup file
     */
    private static File determineBackupName(File base) {
        File dir = base.getParentFile();
        String name = base.getName();
        if (name.endsWith(LOGISIM_EXTENSION)) {
            name = name.substring(0, name.length() - LOGISIM_EXTENSION.length());
        }
        for (int i = 1; i <= 20; i++) {
            String ext = i == 1 ? ".bak" : (".bak" + i);
            File candidate = new File(dir, name + ext);
            if (!candidate.exists()) {
                return candidate;
            }

        }
        return null;
    }

    /**
     * Recover from a backup.
     * @param backup the backup file to recover from
     * @param dest where does the backup file go
     */
    private static void recoverBackup(File backup, File dest) {
        if (backup != null && backup.exists()) {
            if (dest.exists()) {
                dest.delete();
            }

            backup.renameTo(dest);
        }
    }

    /**
     * Loads a Logisim file
     * @param request the requested file
     * @return the completed file object
     */
    LogisimFile loadLogisimFile(File request) throws LoadFailedException {
        File actual = getSubstitution(request);
        for (File fileOpening : filesOpening) {
            if (fileOpening.equals(actual)) {
                logger.error( "An error occurred when opening the file.\n"
                    + getFromLocale("logisimCircularError" ) );
                throw new LoadFailedException(getFromLocale("logisimCircularError",
                        toProjectName(actual)));
            }
        }

        LogisimFile ret = null;
        filesOpening.push(actual);
        try {
            ret = LogisimFile.load(actual, this);
        } catch (IOException e) {
            logger.error( "An error occurred when opening the file.\n"
                + getFromLocale("logisimCircularError" ) );
            throw new LoadFailedException(getFromLocale("logisimLoadError",
                    toProjectName(actual), e.toString()));
        } finally {
            filesOpening.pop();
        }
        ret.setName(toProjectName(actual));
        return ret;
    }

    /**
     * Loads a Java jar file as a library
     * @param request the requested jar file
     * @param string the class name specified in the file
     * @return the loaded library
     */
    Library loadJarFile(File request, String className) throws LoadFailedException {
        File actual = getSubstitution(request);
        // Up until 2.1.8, this was written to use a URLClassLoader, which
        // worked pretty well, except that the class never releases its file
        // handles. For this reason, with 2.2.0, it's been switched to use
        // a custom-written class ZipClassLoader instead. The ZipClassLoader
        // is based on something downloaded off a forum, and I'm not as sure
        // that it works as well. It certainly does more file accesses.

        // Anyway, here's the line for this new version:
        ZipClassLoader loader = new ZipClassLoader(actual);

        // And here's the code that was present up until 2.1.8, and which I
        // know to work well except for the closing-files bit. If necessary, we
        // can revert by deleting the above declaration and reinstating the below.
        /*
        URL url;
        try {
            url = new URL("file", "localhost", file.getCanonicalPath());
        } catch (MalformedURLException e1) {
            throw new LoadFailedException("Internal error: Malformed URL");
        } catch (IOException e1) {
            throw new LoadFailedException(Strings.get("jarNotOpenedError"));
        }
        URLClassLoader loader = new URLClassLoader(new URL[] { url });
        */

        // load library class from loader
        Class<?> retClass;
        try {
            retClass = loader.loadClass(className);
        } catch (ClassNotFoundException e) {
            logger.error( "Class not found" );
            throw new LoadFailedException(getFromLocale("jarClassNotFoundError", className));
        }
        if (!(Library.class.isAssignableFrom(retClass))) {
            logger.error( "Class not library" );
            throw new LoadFailedException(getFromLocale("jarClassNotLibraryError", className));
        }

        // instantiate library
        Library ret;
        try {
            ret = (Library) retClass.newInstance();
        } catch (Exception e) {
            logger.error( "Class not library" );
            throw new LoadFailedException(getFromLocale("jarLibraryNotCreatedError", className));
        }
        return ret;
    }

    //
    // Library methods
    //

    /**
     * Loads a Logisim library
     * @param desc the file descriptor
     * @return the library object
     */
    @Override
    public Library loadLibrary(String desc) {
        return LibraryManager.instance.loadLibrary(this, desc);
    }

    /**
     * Gets a descriptor from a library
     * @param lib the library
     * @return the descriptor
     */
    @Override
    public String getDescriptor(Library lib) {
        return LibraryManager.instance.getDescriptor(this, lib);
    }

    /**
     * Shows an error based on its description, also logs it
     * @param description the error text
     */
    @Override
    public void showError(String description) {
        logger.error( description );
        if (!filesOpening.empty()) {
            File top = filesOpening.peek();
            String init = toProjectName(top) + ":";
            if (description.contains("\n")) {
                description = init + "\n" + description;
            } else {
                description = init + " " + description;
            }
        }

        if (description.contains("\n") || description.length() > 60) {
            int lines = 1;
            for (int pos = description.indexOf('\n'); pos >= 0;
                    pos = description.indexOf('\n', pos + 1)) {
                lines++;
            }
            lines = Math.max(4, Math.min(lines, 7));

            JTextArea textArea = new JTextArea(lines, 60);
            textArea.setEditable(false);
            textArea.setText(description);
            textArea.setCaretPosition(0);

            JScrollPane scrollPane = new JScrollPane(textArea);
            scrollPane.setPreferredSize(new Dimension(350, 150));
            JOptionPane.showMessageDialog(parent, scrollPane,
                    getFromLocale("fileErrorTitle"), JOptionPane.ERROR_MESSAGE);
        } else {
            JOptionPane.showMessageDialog(parent, description,
                    getFromLocale("fileErrorTitle"), JOptionPane.ERROR_MESSAGE);
        }
    }

    /**
     * Show the messages from a file
     * @param source the file to load from
     */
    private void showMessages(LogisimFile source) {
        if (source == null) {
            return;
        }

        String message = source.getMessage();
        while (message != null) {
            JOptionPane.showMessageDialog(parent,
                message, getFromLocale("fileMessageTitle"),
                JOptionPane.INFORMATION_MESSAGE);
            message = source.getMessage();
        }
    }

    //
    // helper methods
    //

    /**
     * Get a file from a name
     * @param name file name
     * @param filter the file name filter
     * @return the file object
     */
    File getFileFor(String name, FileFilter filter) {
        // Determine the actual file name.
        File file = new File(name);
        if (!file.isAbsolute()) {
            File currentDirectory = getCurrentDirectory();
            if (currentDirectory != null) {
                file = new File(currentDirectory, name);
            }

        }
        while (!file.canRead()) {
            // It doesn't exist. Figure it out from the user.
            JOptionPane.showMessageDialog(parent,
                getFromLocale("fileLibraryMissingError",
                    file.getName()));
            JFileChooser chooser = createChooser();
            chooser.setFileFilter(filter);
            chooser.setDialogTitle(getFromLocale("fileLibraryMissingTitle", file.getName()));
            int action = chooser.showDialog(parent, getFromLocale("fileLibraryMissingButton"));
            if (action != JFileChooser.APPROVE_OPTION) {
                throw new LoaderException(getFromLocale("fileLoadCanceledError"));
            }
            file = chooser.getSelectedFile();
        }
        return file;
    }

    /**
     * Conform a file to the current project
     * @param file the destined file
     * @return the project name
     */
    private String toProjectName(File file) {
        String ret = file.getName();
        if (ret.endsWith(LOGISIM_EXTENSION)) {
            return ret.substring(0, ret.length() - LOGISIM_EXTENSION.length());
        } else {
            return ret;
        }
    }

}
TOP

Related Classes of com.cburch.logisim.file.Loader$LogisimFileFilter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.