Controller.java :  » Development » jga-Generic-Algorithms » net » sf » jga » swing » spreadsheet » Java Open Source

Java Open Source » Development » jga Generic Algorithms 
jga Generic Algorithms » net » sf » jga » swing » spreadsheet » Controller.java
// ============================================================================
// $Id: Controller.java,v 1.7 2005/12/17 04:45:03 davidahall Exp $
// Copyright (c) 2004-2005  David A. Hall
// ============================================================================
// The contents of this file are subject to the Common Development and
// Distribution License (CDDL), Version 1.0 (the License); you may not use this 
// file except in compliance with the License.  You should have received a copy
// of the the License along with this file: if not, a copy of the License is 
// available from Sun Microsystems, Inc.
//
// http://www.sun.com/cddl/cddl.html
//
// From time to time, the license steward (initially Sun Microsystems, Inc.) may
// publish revised and/or new versions of the License.  You may not use,  
// distribute, or otherwise make this file available under subsequent versions 
// of the License.
// 
// Alternatively, the contents of this file may be used under the terms of the 
// GNU Lesser General Public License Version 2.1 or later (the "LGPL"), in which
// case the provisions of the LGPL are applicable instead of those above. If you 
// wish to allow use of your version of this file only under the terms of the 
// LGPL, and not to allow others to use your version of this file under the 
// terms of the CDDL, indicate your decision by deleting the provisions above 
// and replace them with the notice and other provisions required by the LGPL. 
// If you do not delete the provisions above, a recipient may use your version 
// of this file under the terms of either the CDDL or the LGPL.
// 
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
// ============================================================================

package net.sf.jga.swing.spreadsheet;

import java.awt.Component;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.MessageFormat;
import javax.swing.Action;
import javax.swing.JFileChooser;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import net.sf.jga.fn.BinaryFunctor;
import net.sf.jga.fn.Generator;
import net.sf.jga.fn.UnaryFunctor;
import net.sf.jga.fn.adaptor.Constant;
import net.sf.jga.fn.adaptor.ConstantBinary;
import net.sf.jga.fn.adaptor.ConstantUnary;
import net.sf.jga.fn.adaptor.Identity;
import net.sf.jga.fn.adaptor.Project2nd;
import net.sf.jga.fn.property.InvokeNoArgMethod;
import net.sf.jga.parser.FunctorParser;
import net.sf.jga.parser.ParseException;
import net.sf.jga.swing.GenericAction;

/**
 * Provides for generally common operations on a spreadsheet when it is in
 * an application.
 * <p>
 * Copyright &copy; 2004-2005  David A. Hall
 * @author <a href="mailto:davidahall@users.sf.net">David A. Hall</a>
 */

public class Controller{

    // The spreadsheet being controlled
    private Spreadsheet _sheet;

    // A parser for constructing various functors for the sheet
    private FunctorParser _parser;

    // The functor executed when the user must be prompted for simple information
    private BinaryFunctor<String,String,String> _promptFn =
        new ConstantBinary<String,String,String>("");

    // The functor executed when the user is asked a yes/no/cancel question
    private BinaryFunctor<String,String,Integer> _confirmFn = 
        new ConstantBinary<String,String,Integer>(null);
    
    // The functor executed to report errors to the user
    private BinaryFunctor<String, String, ?> _errorFn =
        new ConstantBinary<String,String,String>("");

    // The functor executed to load a spreadsheet
    private UnaryFunctor<Spreadsheet,Integer> _loadFn =
        new ConstantUnary<Spreadsheet,Integer>(null);

    // The functor executed to save a spreadsheet
    private BinaryFunctor<Spreadsheet,Boolean,Integer> _saveFn =
        new ConstantBinary<Spreadsheet,Boolean,Integer>(null);

    public static final int YES_OPTION    = JOptionPane.YES_OPTION;
    public static final int NO_OPTION     = JOptionPane.NO_OPTION;
    public static final int CANCEL_OPTION = JOptionPane.CANCEL_OPTION;

    
    /**
     * Builds a Controller to control the given sheet widget.
     */
    public Controller(Spreadsheet sheet) {
        _sheet = sheet;
        _parser = new FunctorParser();
        _parser.bindThis(this);
    }


    /**
     * Prompts the user for a string value.
     * @param msg the description of the string to be shown to the user
     * @param val the default or existing value of the string
     */
    public String prompt(String msg, String val) { return _promptFn.fn(msg, val); }

    
    /**
     * Sets the functor used to prompt the user for simple information.
     */
    public void setPromptFunctor(BinaryFunctor<String,String,String> fn) {
        _promptFn = fn;
    }

    
    /**
     * Asks the user a Yes/No/Cancel question.
     * @return a value taken fro
     */

    public int confirm(String msg, String title) { return _confirmFn.fn(msg, title); }

    
    /**
     * Sets the functor used to ask the user a yes/no/cancel question
     */
    
    public void setConfirmFunctor(BinaryFunctor<String,String,Integer> fn) { _confirmFn = fn; }

    
    /**
     * Displays an error message to the user
     */

    public void notify(String msg, String title) { _errorFn.fn(msg, title); }


    /**
     * Sets the functor used to display an error message.
     */
    public void setErrorFunctor(BinaryFunctor<String,String,?> fn) { _errorFn = fn; }

    
    /**
     * Loads a spreadsheet
     */

    public int loadSheet(Spreadsheet sheet) {
        return _loadFn.fn(sheet);
    }

    /**
     * Sets the functor used to get an output stream
     */

    public void setLoadFunctor(UnaryFunctor<Spreadsheet,Integer> fn) { _loadFn = fn; }
                                           
   
    /**
     * Saves a spreadsheet
     */

    public int saveSheet(Spreadsheet sheet, boolean prompt) {
        return _saveFn.fn(sheet,prompt);
    }

    
    /**
     * Sets the functor used to get an output stream
     */

    public void setSaveFunctor(BinaryFunctor<Spreadsheet,Boolean,Integer> fn) { _saveFn = fn; }
                                           
    // ----------------------
    // Spreadsheet UI Methods
    // ----------------------

    /**
     * Returns an Action that resets the spreadsheet to default state.
     */
    public Action getFileNewCmd() {
        return new GenericAction(parseAction("this.newWorksheet()"), "New");
    }

    
    /**
     * Returns an Action that sets the spreadsheet to the contents of a file.
     */
    public Action getFileOpenCmd() {
        return new GenericAction(parseAction("this.openFile()"), "Open");
    }


    // TODO: saveFile should only be enabled when the file is dirty
 
    /**
     * Returns an Action that saves the spreadsheet to the file from whence it came.
     */
    public Action getFileSaveCmd() {
        return new GenericAction(parseAction("this.saveFile()"), "Save");
    }


    // TODO: saveFileAs should only be enabled when the file is dirty

    /**
     * Returns an Action that saves the spreadsheet to a file.
     */
    public Action getFileSaveAsCmd() {
        return new GenericAction(parseAction("this.saveFileAs()"), "Save As...");
    }

    
    /**
     * Returns an Action that changes the default cell format.
     */
    public Action getDefaultEditableCmd() {
        GenericAction cmd = new GenericAction(new Identity<ActionEvent>(), "Editable");
        cmd.setEnabled(false);
        return cmd;
    }

    /**
     * Returns an Action that changes the default cell type.
     */
    public Action getDefaultTypeCmd() {
        return new GenericAction(parseAction("this.setDefaultType()"), "Cell Type");
    }

    /**
     * Returns an Action that changes the default cell type.
     */
    public Action getDefaultValueCmd() {
        return new GenericAction(parseAction("this.setDefaultValue()"), "Cell Value");
    }

    /**
     * Returns an Action that changes the default cell format.
     */
    public Action getDefaultFormatCmd() {
        GenericAction cmd = new GenericAction(new Identity<ActionEvent>(), "Format");
        cmd.setEnabled(false);
        return cmd;
    }

    /**
     * Returns an Action that changes the default cell renderer.
     */
    public Action getDefaultRendererCmd() {
        GenericAction cmd = new GenericAction(new Identity<ActionEvent>(), "Renderer");
        cmd.setEnabled(false);
        return cmd;
    }

    /**
     * Returns an Action that changes the default cell editor.
     */
    public Action getDefaultEditorCmd() {
        GenericAction cmd = new GenericAction(new Identity<ActionEvent>(), "Editor");
        cmd.setEnabled(false);
        return cmd;
    }

    /**
     * Returns an Action that resizes the worksheet.
     */
    public Action getSheetColumnsCmd() {
        return new GenericAction(parseAction("this.setColumnCount()"), "Set Column Count");
    }

    /**
     * Returns an Action that resizes the worksheet.
     */
    public Action getSheetRowsCmd() {
        return new GenericAction(parseAction("this.setRowCount()"), "Set Row Count");
    }

    /**
     * Returns an Action that allows the user to import a class.
     */
    public Action getImportClassCmd() {
        GenericAction cmd = new GenericAction(new Identity<ActionEvent>(), "Import Class");
        cmd.setEnabled(false);
        return cmd;
    }

    /**
     * Returns an Action that renames the current cell.
     */
    public Action getCellRenameCmd() {
        String cellNameExp = "this.setCellName(x.getSelectedRow(),x.getSelectedColumn())";
        try {
            UnaryFunctor<ActionEvent,Object> cellNameFn =
                new Project2nd<ActionEvent,Object>()
                    .generate2nd(_parser.parseUnary(cellNameExp, Spreadsheet.class).bind(_sheet));
                             
            return new GenericAction(cellNameFn, "Set Name");
        }
        catch (ParseException x) {
            x.printStackTrace();
            GenericAction cmd =
                new GenericAction(new ConstantUnary<ActionEvent,Object>(null), "Set Name");
            cmd.setEnabled(false);
            return cmd;
        }
    }

    /**
     * Returns an Action that reformats the current cell.
     */
    public Action getCellFormatCmd() {
        GenericAction cmd = new GenericAction(new Identity<ActionEvent>(), "Set Format");
        cmd.setEnabled(false);
        return cmd;
    }

    /**
     * Returns an Action that changes the type of the current cell.
     */
    public Action getCellTypeCmd() {
        GenericAction cmd = new GenericAction(new Identity<ActionEvent>(), "Set Type");
        cmd.setEnabled(false);
        return cmd;
    }
    

    /**
     * Returns an Action that changes the type of the current cell.
     */
    public Action getCellRendererCmd() {
        GenericAction cmd = new GenericAction(new Identity<ActionEvent>(), "Set Renderer");
        cmd.setEnabled(false);
        return cmd;
    }
    

    /**
     * Returns an Action that changes the type of the current cell.
     */
    public Action getCellEditorCmd() {
        GenericAction cmd = new GenericAction(new Identity<ActionEvent>(), "Set Editor");
        cmd.setEnabled(false);
        return cmd;
    }
    

    /**
     * Returns an Action that changes the type of the current cell.
     */
    public Action getCellValidatorCmd() {
        GenericAction cmd = new GenericAction(new Identity<ActionEvent>(), "Set Validator");
        cmd.setEnabled(false);
        return cmd;
    }
    
    // ----------------------
    // Worksheet Methods
    // ----------------------

    /**
     * Prompts the user for the cell type.
     */
    public void setDefaultType() {
        String prompt = "Enter default type for uninitialized cells";
        String name = _promptFn.fn(prompt, _sheet.getDefaultCellType().getName());
        if (name == null)
            return;

        try {
            _sheet.setDefaultCellType(Class.forName(name));
        }
        catch (ClassNotFoundException x) {
            String fmt = "Class {0} not found";
            String msg = MessageFormat.format(fmt, new Object[]{ x.getMessage() });
            _errorFn.fn(msg, "ClassNotFoundException");
        }
    }
                                     
    /**
     * Prompts the user for the number of columns
     */
    public void setColumnCount() {
        String prompt = "Enter the number of columns";
        String countstr = _promptFn.fn(prompt, String.valueOf(_sheet.getColumnCount()));
        if (countstr == null)
            return;

        try {
            _sheet.setColumnCount(Integer.parseInt(countstr));
        }
        catch (NumberFormatException x) {
            String fmt = "{0} isn't a number";
            String msg = MessageFormat.format(fmt, new Object[]{ countstr });
            _errorFn.fn(msg, "NumberFormatException");
        }
    }
                                     
    /**
     * Prompts the user for the number of rows
     */
    public void setRowCount() {
        String prompt = "Enter the number of rows";
        String countstr = _promptFn.fn(prompt, String.valueOf(_sheet.getRowCount()));
        if (countstr == null)
            return;

        try {
            _sheet.setRowCount(Integer.parseInt(countstr));
        }
        catch (NumberFormatException x) {
            String fmt = "{0} isn't a number";
            String msg = MessageFormat.format(fmt, new Object[]{ countstr });
            _errorFn.fn(msg, "NumberFormatException");
        }
    }
                                     
    // TODO: need to save the formula and use it in the prompt, instead of the current
    // default value represented as a string.  If the user, for example, sets the default
    // value to "new java.util.Date()", on the next prompt, he'll get the point in time
    // in which the expression was evaluated, which itself is not parsable.

    /**
     * Prompts the user for the default cell value.
     */
    public void setDefaultValue() {
        String prompt = "Enter default value for uninitialized cells";
        String exp = _promptFn.fn(prompt, ""+_sheet.getDefaultCellValue());
        if (exp == null)
            return;

        try {
            Generator gen = _sheet.getParser().parseGenerator(exp);
            _sheet.setDefaultCellValue(gen.gen());
        }
        catch (ParseException x) {
            _errorFn.fn(x.getMessage(), getExceptionName(getRootCause(x)));
        }
    }

    // ----------------------
    // Cell Methods
    // ----------------------
    /**
     * Prompts the user for the name of the designated cell.
     */
    public void setCellName(int row, int col) {
        String prompt = "Enter name for cell("+row+","+col+")";
        Cell cell = _sheet.getCellIfPresent(row, col);
        String oldName = (cell != null) ? cell.getName() : "";
        String name = _promptFn.fn(prompt, oldName);
        if (name != null && !name.equals(oldName))
            _sheet.setCellName(name, row, col);
    }
                                     

    // ----------------------
    // Spreadsheet IO Methods
    // ----------------------

    /**
     * Creates a new, empty spreadsheet with default settings.
     */
    public void newWorksheet() {
        int ans = JOptionPane.YES_OPTION;
        if (isSheetDirty()) {
            ans = promptAndSave();
        }
     
        if (ans != JOptionPane.CANCEL_OPTION) {
            _sheet.clear();
            setSheetSource(null);
            setSheetDirty(false);
        }
    }

    
    /**
     * Prompts the user for a file to open, and sets the spreadsheet to the file's contents.
     */
    public int openFile() { return loadSheet(_sheet); }


    /**
     */
    public int saveFile() { return saveSheet(_sheet, false); }

                                     
    /**
     */
    public int saveFileAs() { return saveSheet(_sheet, true); }

 
    /**
     * Prompts the user using a supplied functor, and returns one of YES, NO, or CANCEL
     */
    public int promptAndSave() {
        String format = "save {0}?";
        Object name = getSheetSource();
        if (name == null)
            name = "worksheet";
                
        String msg = MessageFormat.format(format, new Object[]{ name });
        int ans = confirm(msg,msg);
        if (ans == Controller.YES_OPTION) {
            int choice = saveFile();
            if (choice == Controller.CANCEL_OPTION || choice == JFileChooser.ERROR_OPTION) {
                ans = Controller.CANCEL_OPTION;
            }
        }
        return ans;
    }

    // ------------------------
    // Implementation Details
    // ------------------------

    private final String DIRTY_PROP = "dirty";
    
    public boolean isSheetDirty() {
        Object dirtyProp = _sheet.getClientProperty(DIRTY_PROP);
        return Boolean.TRUE.equals(dirtyProp);
    }

    public void setSheetDirty(boolean flag) {
        _sheet.putClientProperty(DIRTY_PROP, Boolean.valueOf(flag));
    }

    
    private final String SOURCE_PROP = "source";

    public URL getSheetSource()  {
        Object sourceURL = _sheet.getClientProperty(SOURCE_PROP);
        if (sourceURL == null)
            return null;

        if (sourceURL instanceof URL)
            return (URL) sourceURL;

        try {
            return new File(new File("."), "worksheet1.hwks").toURL();
        }
        catch (MalformedURLException x) {
            x.printStackTrace();
            return null;
        }
    }

    public void setSheetSource(URL url) {
//         System.out.println("setSheetSource(" + url + ")");
        _sheet.putClientProperty(SOURCE_PROP, url);
    }
    
    private UnaryFunctor<ActionEvent,?> parseAction(String exp) {
        try {
            return new Project2nd<ActionEvent,Object>()
                .generate2nd(_parser.parseGenerator(exp));
        }
        catch (ParseException x) {
            x.printStackTrace();
            // TODO: return some default that won't kill us
            return null;
        }
    }

    // ===============================================================

    static Throwable getRootCause(Throwable t) {
        for (Throwable t1 = t.getCause(); t1 != null; t1 = t.getCause())
            t = t1;

        return t;
    }

    static String getExceptionName(Throwable t) {
        String fqcn = t.getClass().getName();
        return fqcn.substring(fqcn.lastIndexOf(".") + 1);
    }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.