Package ket

Source Code of ket.MathCollection

/*
* Copyright (C) 2011  Alasdair C. Hamilton
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

package ket; // Move to ket.math?


import java.io.*;
import java.util.Vector;

import ket.math.*;
import ket.math.convert.*;
import ket.math.purpose.Text;
import ket.display.*;
import ketUI.Ket;


/**
* There are various levels of abstraction between low level graphical and
* keyboard interactions and the high level, abstract mathematical model.  This
* class provides a boundary between the two providing practical tools for
* loading and saving equations, but also provides an abstract equation model
* as a graph of lists of equations.  Additionally, searching and substitution
* methods are provided.
*/
public class MathCollection implements SelectionState {

  final CursorSelection cursorSelection;
  final RangeSelection rangeSelection;
  final ColumnSelection columnSelection;
  final Drag drag;
  Selection selection;
  Selection past;
  Address from;
  Address to;

  final KnownArguments knownArguments;
  // If this is null then the value has changed and so no longer needs to
  // be compared.
  Integer fileHashCode;
  Undo undo;

  /**
   * Check to see if the equation list has been changed since the last save.
   */
  public boolean containsUnsavedChanges() {
    if (fileHashCode==null) {
      return true;
    }
    int currentHashCode = getEquationList().hashCode();
    if (fileHashCode.intValue()!=currentHashCode) {
      // Set the original value to null to avoid having to
      // check every time a new change is made.  This also
      // avoids unlikely but possible hashCode coincidences.
      fileHashCode = null;
      return true;
    } else {
      return false;
    }
  }

  public int hashCode() {
    return getEquationList().hashCode();
  }

  public KnownArguments getKnownArguments() {
    return knownArguments;
  }


  /////////////
  // FILE IO //
  /////////////

  /**
   * Given the location in which a file is stored, read in and parse a
   * given text file and append each equation to this list.  The current
   * element is set as the last equation to be loaded.
   * The program handles exceptions by aborting, displaying an
   * error message to standard output and returning false.
   * @return Returns <I>true</I> if the program successfully opened and
   * read to the end of the file.  No equations need be read.  In
   * contrast, if the file is missing or cannot be opened or read from,
   * then exceptions are caught and <I>false</I> is instead returned.
   */
  public boolean read(String filename) {
    try {
      readStream(new FileReader(filename));
      return true;
    } catch (IOException e) {
      Ket.out.println(" !!! Failure to read file: " + filename + ". !!!");
      Ket.out.println("exception: " + e);
      e.printStackTrace();
      return false;
    }
  }

  private void readStream(Reader reader) throws IOException {
    EquationList equationList = getEquationList();
    BufferedReader bufferedReader = new BufferedReader(reader);
    Vector<Argument> rows = new Vector<Argument>();
    String line = bufferedReader.readLine();
    while (line!=null) {
      line = readAppendedLines(bufferedReader, line);
      if (line.trim().indexOf('\t')!=-1) { // A table of tab-separated values
        Ket.out.println("<row>");
        Ket.out.println("tsv: " + line);
        String[] elms = line.trim().split("\t");
        Branch matrix = new Branch(Function.MATRIX);
        Ket.out.println("<LOOP>");
        for (String element : elms) {
          Ket.out.print("\t\telement = '" + element + "'");
          Ket.out.println("<PARSE>");
          Argument next = ArgumentParser.parseArgument(element, knownArguments, null, this);
          Ket.out.println("</PARSE>");
          Ket.out.print(" -> ");
          Ket.out.println(next);
          if (next!=null) {
            matrix.append(next);
          }
        }
        Ket.out.println("</LOOP>");
        Ket.out.println("\trow: " + matrix);
        rows.add(matrix);
        Ket.out.println("</row>");
      } else {
        appendTable(rows);
        Equation equation = processLine(line);
        equationList.addLast(equation);
        setCurrent(equation.getRoot());
      }
      line = bufferedReader.readLine();
    }
    appendTable(rows);
    fileHashCode = new Integer(equationList.hashCode());
    undo = new Undo(equationList);
    bufferedReader.close();
  }

  /**
   * Append an existing table, if complete.
   */
  private void appendTable(Vector<Argument> rows) {
    if (rows==null || rows.size()==0) return;
    Equation equation = new Equation(new Branch(Function.VECTOR, rows));
    getEquationList().addLast(equation);
    setCurrent(equation.getRoot());
    rows.clear();
  }

  private boolean isWrappedLine(String line) {
    int len = line.length();
    return len>0 && line.charAt(len-1)=='\\';
  }

  /**
   * Keep reading from the buffer reader while the lines are wrapped,
   * that is they end with a backslash.  The resulting line (without the
   * backslash) is then returned.
   */
  private String readAppendedLines(BufferedReader bufferedReader, String line) throws IOException {
    if (!isWrappedLine(line)) {
      return line;
    }
    int len = line.length();
    line = len>1 ? line.substring(0, len-1) : "";
    String next = "";
    while (true) {
      next = bufferedReader.readLine();
      if (next==null) {
        return line;
      } else if (!isWrappedLine(next)) {
        return line + next;
      } else if (next.length()>1) {
        line += next.substring(0, next.length()-1);
      }
    }
  }

  /**
   * Template files are stored within Ket.jar.  Note that all filenames
   * should be absolute paths relative to the root of the jar file!
   */
  public boolean readJar(String filename) { // TODO: Move to a more general location such as Ket.
    // Note: Jar filenames always start with a slash.
    try {
      InputStream inputStream = MathCollection.class.getResourceAsStream(filename);
      if (inputStream!=null) {
        readStream(new InputStreamReader(inputStream));
        return true;
      } else {
        Ket.out.println(" !!! File not found within Jar !!! ");
        Ket.out.println(filename);
        return false;
      }
    } catch (IOException e) {
      Ket.out.println(" !!! Failure to read jar file: '" + filename + "'. !!!");
      Ket.out.println("exception: " + e);
      e.printStackTrace();
      return false;
    }
  }

  /**
   * Process the next line from a text file and add it to the mathList as
   * an equation or as a line of plain text.
   */
  public Equation processLine(String line) {
    // An equation is denoted by a tab or at least for spaces of indentation.
    boolean equationLike = line.matches("[ {4}\t].*");
    // Labels follow a # symbol.
    int labelIndex = line.lastIndexOf('#');
    if (labelIndex!=-1) {  // The line ends in a label.
      String content = line.substring(0, labelIndex).trim();
      String label = line.substring(labelIndex+1).trim();
      Argument root = getRoot(content, equationLike);
      return new Equation(root, label);
    } else { // The line is not labelled.
      Argument root = getRoot(line, equationLike);
      return new Equation(root);
    }
  }

  private Argument getRoot(String content, boolean equationLike) {
    if (equationLike && !content.matches("\\s*")) {
      return ArgumentParser.parseArgument(content, knownArguments, null, this);
    }
    String text = content.trim();
    if (text.matches("\\*.+\\*")) { // *heading* or *a longer title*
      text = text.substring(1, text.length()-1); // ish
      Token token = new Token(new Text(text));
      token.setBold(true);
      return token;
    } else {
      return new Token(new Text(text));
    }
  }

  private boolean saveRoot(Equation equation, BufferedWriter bufferedWriter) throws IOException {
    Argument root = equation.getRoot();
    Argument[][] table = null;
    if (root instanceof Branch) {
      Vector<Argument> kids = root.asBranch().getChildren();
      if (root.getFunction()==Function.VECTOR) {
        table = ArgumentTools.asTable(kids, false);
      } else if (root.getFunction()==Function.MATRIX) {
        table = ArgumentTools.asTable(kids, true);
      }
    }
    if (table!=null) {
      // A table
      for (Argument[] row : table) {
        for (Argument element : row) {
          bufferedWriter.write("\t" + element);
        }
        bufferedWriter.write("\n");
      }
      return true;
    } else {
      // A regular equation.
      bufferedWriter.write("" + equation.toString() + "\n");
      return false;
    }
  }

  /**
   * Save text to a given filename, returning true if the file was saved correctly.
   */
  public boolean writeText(String filename) {
     try {
       FileWriter fileWriter = new FileWriter(filename, false);
       BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
       for (Equation equation : getEquationList().getEquations()) {
         saveRoot(equation, bufferedWriter);
       }
       bufferedWriter.close();
       fileHashCode = new Integer(getEquationList().hashCode());
       return true;
     } catch (IOException e) {
      Ket.out.println(" !!! Exception thrown during saving to file !!! ");
      Ket.out.println(e);
      e.printStackTrace();
      return false;
     }
  }

  /**
   * Save text to a given filename, returning true if the file was saved correctly.
   */
  public boolean writeLatex(String filename) {
     try {
       FileWriter fileWriter = new FileWriter(filename, false);
       BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
       bufferedWriter.write(LatexTools.LATEX_FILE_START);
       boolean wasPlainText = false;
       for (Equation equation : getEquationList().getEquations()) {
         if (wasPlainText && equation.isPlainText()) {
           // Add a blank line between lines of
           // text which are assumed to represent
           // paragraphs.
           bufferedWriter.write("\n");
         }
         bufferedWriter.write(equation.toLatex() + "\n");
         wasPlainText = equation.isPlainText();
       }
       bufferedWriter.write(LatexTools.LATEX_FILE_END);
       bufferedWriter.close();
       return true;
     } catch (IOException e) {
      Ket.out.println(" !!! Exception thrown during saving to file !!! ");
      Ket.out.println(e);
      e.printStackTrace();
      return false;
     }
  }

  /**
   * Save text to a given filename, returning true if the file was saved correctly.
   */
  public boolean writeClojure(String filename) {
     try {
       FileWriter fileWriter = new FileWriter(filename, false);
       BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
       for (Equation equation : getEquationList().getEquations()) {
         bufferedWriter.write(equation.toPrefixNotation()+"\n");
       }
       bufferedWriter.close();
       return true;
     } catch (IOException e) {
      Ket.out.println(" !!! Exception thrown during saving to file !!! ");
      Ket.out.println(e);
      e.printStackTrace();
      return false;
     }
  }

  /**
   * Save text to a given filename, returning true if the file was saved correctly.
   */
  public boolean writeHTML(String filename) {
     try {
       FileWriter fileWriter = new FileWriter(filename, false);
       BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);

       bufferedWriter.write(HTMLTools.HTML_FILE_START);
       boolean wasPlainText = false;
       for (Equation equation : getEquationList().getEquations()) {
         if (wasPlainText && equation.isPlainText()) {
           // Only add a blank line between paragraphs.
           bufferedWriter.write("</P>\n");
           bufferedWriter.write("\n");
           bufferedWriter.write("<P>\n");
         }
         bufferedWriter.write(equation.toHTML());
         wasPlainText = equation.isPlainText();
       }
       bufferedWriter.write(HTMLTools.HTML_FILE_END);
       bufferedWriter.close();
       return true;
     } catch (IOException e) {
      Ket.out.println(" !!! Exception thrown during saving to file !!! ");
      Ket.out.println(e);
      e.printStackTrace();
      return false;
     }
  }

  // ---------------- SELECTION RELATED STUFF ----------------

  public MathCollection(KnownArguments knownArguments) {
    this.knownArguments = knownArguments;
    cursorSelection = new CursorSelection(this); // This implicitly creates a new equation list.
    rangeSelection = new RangeSelection(cursorSelection);
    columnSelection = new ColumnSelection(cursorSelection);
    selection = cursorSelection;
    fileHashCode = new Integer(-1); // Default to 'has unsaved changes'.
    undo = new Undo();
    drag = new Drag(cursorSelection);
  }
 
  public Drag getDrag() {
    return drag;
  }

  public EquationList getEquationList() {
    return selection.getEquationList();
  }

  public Undo getUndo() {
    return undo;
  }

  public boolean undoStackIsUpToDate() {
    return undo.isUnchanged(selection.getEquationList());
  }

  public void updateUndoStack() {
    Address cursor = getCurrent().getAddress();
    undo.recordChange(selection.getEquationList(), cursor);
  }

  public boolean undo() {
    //- int index = getCurrent().getEquationIndex();
    EquationList el = getEquationList();
    Address address = undo.undo(el);
    if (address==null) {
      Ket.out.println(" !!! NULL UNDO CURSOR LOCATION !!! ");
      return false;
    }
    Argument current = el.relativeAddress(address);
    if (current==null) {
      int equationIndex = bound(0, address.getEquationIndex(), el.size()-1);
      selection.setCurrent(el.getEquation(equationIndex).getRoot());
      return true;
    }
    selection.setCurrent(current);
    return true;
  }

  private int bound(int from, int index, int to) {
    if (index<from) {
      return from;
    } else if (to<index) {
      return to;
    } else {
      return index;
    }
  }

  public boolean redo() {
    EquationList el = getEquationList();
    Address address = undo.redo(el);
    if (address==null) {
      Ket.out.println(" !!! NULL REDO CURSOR LOCATION !!! ");
      return false;
    }
    Argument current = el.relativeAddress(address);
    selection.setCurrent(current!=null ? current : el.getEquation(0).getRoot());
    return false; // Return type: fail or unchanged?
  }

  public void setColumnSelection() {
    past = columnSelection;
    selection = columnSelection;
    columnSelection.selectFromCurrent();
    from = selection.getCursor().getAddress();
  }

  public void setRangeSelection() {
    past = rangeSelection;
    selection = rangeSelection;
    rangeSelection.selectFromCurrent();
    from = selection.getCursor().getAddress();
  }

  public void setCursorSelection() {
    selection = cursorSelection;
    to = selection.getCursor().getAddress();
  }

  private Argument getAddress(Address address) { // TODO: Use in undo.
    if (address==null) {
      return null;
    }
    return getEquationList().relativeAddress(address);
  }

  public void reselection() {
    if (to==null || from==null) {
      Ket.out.println(" !!! No region !!! ");
    } else if (past==rangeSelection) {
      setCurrent(getAddress(from));
      selection = rangeSelection;
      rangeSelection.selectFromCurrent();
      setCurrent(getAddress(to));
    } else if (past==columnSelection) {
      setCurrent(getAddress(from));
      selection = columnSelection;
      columnSelection.selectFromCurrent();
      setCurrent(getAddress(to));
    } else {
      Ket.out.println(" !!! No known past !!! ");
    }
  }

  public void toggleColumnSelection() {
    if (isSingleSelectionSet()) {
      setColumnSelection();
    } else {
      setCursorSelection();
    }
  }

  public void toggleRangeSelection() {
    if (isSingleSelectionSet()) {
      setRangeSelection();
    } else {
      setCursorSelection();
    }
  }

  //- public void reselect() {
  //- }

  public boolean isSingleSelectionSet() {
    return selection==cursorSelection;
  }

  public boolean isRangeSelectionSet() {
    return selection==rangeSelection;
  }

  public boolean isColumnSelectionSet() {
    return selection==columnSelection;
  }

  public Selection getSelection() {
    return selection;
  }

  public Cursor getCursor() {
    return selection.getCursor();
  }

  public CursorSelection getCursorSelection() {
    return cursorSelection;
  }
 
  public RangeSelection getRangeSelection() {
    return rangeSelection;
  }

  /**
   * Provides access to information about the current selection without the ability to change anything.
   */
  public Highlight getHighlight() {
    return getSelection();
  }

  private Argument getCurrent() {
    return selection.getCurrent();
  }

  private void setCurrent(Argument argument) {
    selection.setCurrent(argument);
  }
   
}
TOP

Related Classes of ket.MathCollection

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.