📄 sheet.java
字号:
/*
* MC2 -- j2me spreadsheet
*
* Copyright (c) 2004-2006 Michael Zemljanukha (mixaz@mail.ru)
*
* 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 2 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, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.wapindustrial.calc;
import java.util.*;
import java.io.*;
//#ifndef NOGUI
import javax.microedition.rms.*;
//#endif
public final class Sheet extends LispObject {
static final int AXIS_X=0, AXIS_Y=1;
public boolean isChanged = false;
Hashtable cells; /* of Cell type */
Hashtable rows,columns; /* of RowColumn type */
Hashtable changedCells; // of Cell type */
Vector refTable; // of Dependency type
// default row/column width/height, in font characters (not pixels!) / 64
static final int fontDivider = 64;
int defaultWidthHeight[]= { 6*fontDivider, 1*fontDivider };
// visible range of sheet
int visibleX, visibleY;
public String name; // sheet name
public Sheet() {
name = "untitled";
visibleX = 8;
visibleY = 32;
clearSheet();
}
/* =========================
* complex Atom interface
*/
public int typeNumber() {
return TYPE_SHEET;
}
/* (non-Javadoc)
* @see com.wapindustrial.calc.LispObject#listSize()
*/
public int listSize() {
return 7;
}
/* (non-Javadoc)
* @see com.wapindustrial.calc.LispObject#getPart(int)
*/
LispObject getPart(int item) {
switch(item) {
case 0:
return Bfunc.BFUNC.table[Bfunc.INDEX_SHEET_CREATE];
case 1:
return new StringAtom( name );
case 2:
return new QuotedList( columns );
case 3:
return new QuotedList( rows );
case 4:
return new QuotedList( cells );
case 5:
return new QuotedList(
new LispObject[] {
new ShortAtom( defaultWidthHeight[AXIS_X] ),
new ShortAtom( defaultWidthHeight[AXIS_Y] )
}
);
case 6:
return new QuotedList(
new LispObject[] {
new ShortAtom( visibleX ),
new ShortAtom( visibleY )
}
);
}
// will throw error
return super.getPart(item);
}
/* (non-Javadoc)
* @see com.wapindustrial.calc.LispObject#setPart(int, com.wapindustrial.calc.LispObject)
*/
// LispObject setPart(int item, LispObject newvalue)
// throws IllegalArgumentException {
// switch(item) {
// case 1:
// name = ((StringAtomBase)newvalue).getValue();
// break;
// case 2:
// columns = ((QuotedList)newvalue).toHashtable();
// break;
// case 3:
// return new QuotedList( rows );
// case 4:
// return new QuotedList( cells );
// case 5:
// return new QuotedList(
// new LispObject[] {
// new ShortAtom( defaultWidthHeight[AXIS_X] ),
// new ShortAtom( defaultWidthHeight[AXIS_Y] )
// }
// );
// case 6:
// return new QuotedList(
// new LispObject[] {
// new ShortAtom( visibleX ),
// new ShortAtom( visibleY )
// }
// );
// }
// // will throw error
// return super.getPart(item);
// }
public static Sheet sheetFromList( FunctorList fl ) throws EvaluateException {
Sheet sheet = new Sheet();
sheet.name = ((StringAtomBase)fl.getArgument1()).getValue();
sheet.columns = ((QuotedList)fl.evaluateArg2()).toHashtable();
sheet.rows = ((QuotedList)fl.evaluateArg3()).toHashtable();
Hashtable cells = ((QuotedList)fl.evaluateArgN(4)).toHashtable();
// append to cells and recalculate
for (Enumeration e = cells.elements() ; e.hasMoreElements() ;) {
Cell cl = (Cell) e.nextElement();
sheet.putCell( cl );
}
// sheet.changedCells = ((QuotedList)fl.evaluateArgN(4)).toHashtable();; // duplicate [4]!
int len = fl.listSize();
if( len > 5 ) {
QuotedList widthheight = fl.getQuotedList(5);
sheet.defaultWidthHeight[AXIS_X] = widthheight.getShort(0);
sheet.defaultWidthHeight[AXIS_Y] = widthheight.getShort(1);
}
if( len > 6 ) {
QuotedList widthheight = fl.getQuotedList(6);
sheet.visibleX = widthheight.getShort(0);
sheet.visibleY = widthheight.getShort(1);
}
// try { // references can be recalculated only for active sheet :(
// sheet.recalculateChangedCells( null );
// }
// catch( CircularReferenceException ee ) {
// throw new EvaluateException(ee.getMessage());
// }
sheet.isChanged = false;
return sheet;
}
public void clearSheet() {
cells = new Hashtable();
changedCells = new Hashtable();
rows = new Hashtable();
columns = new Hashtable();
refTable = new Vector();
}
/* =========================================
* Misc
*/
public final boolean isEmpty( int i, int j ) {
return getCell( i,j ).isEmpty(); // don't compare with CELL_EMPTY since there may be an expression
}
// public final boolean isFormula( int i, int j ) {
// return getCell( i,j ).isFormula();
// }
public int coord( int axis, int I ) {
int Y = 0;
Hashtable tb = axis == AXIS_Y ? rows : columns;
int default_size = defaultWidthHeight[axis];
for( int i=0; i<I; i++ ) {
RowColumn rc = (RowColumn) tb.get( new Integer(i) );
if( rc != null )
Y += rc.width_height;
else
Y += default_size;
}
return Y;
}
/* =================================================================
* references
*/
// point p2 depends on range r1
private void addReference( int i1, int j1, int i2, int j2, int i3, int j3 ) {
// test if it's already in the list
for( int i=refTable.size()-1; i>=0; i-- ) {
Dependency dep = (Dependency) refTable.elementAt( i );
if(
dep.i1 == i1 && dep.j1 == j1 &&
dep.i2 == i2 && dep.j2 == j2 &&
dep.i3 == i3 && dep.j3 == j3
)
return;
}
refTable.addElement( new Dependency( i1,j1,i2,j2,i3,j3 ) );
}
public Cell clearCell( int I, int J ) {
// remove references from this cell
for( int i=refTable.size()-1; i>=0; i-- ) {
Dependency dep = (Dependency) refTable.elementAt( i );
if( dep.i3 == I && dep.j3 == J )
refTable.removeElementAt( i );
}
Cell cl = getCell( I,J );
cells.remove( cl );
changedCells.put( cl, cl );
return cl;
}
// returns true if value has been changed
// does not recalculate dependencies!
public void setFormula( int I, int J, String ss ) throws ParseException, EvaluateException {
// evaluate the expression
LispObject rr = LispObject.parseSExp( ss );
// calculate references
rr = evaluateRef( rr, I, J );
// LispObject rrr = rr.evaluateSExp();
// no exception occured
Cell oldcell = getCell( I, J );
Cell cl = new Cell( I, J, rr, oldcell.format );
putCell( cl );
}
// do not forget to evaluateRef() and to calculate value
void putCell( Cell cl ) {
// remove old value from dependencies and sheets
clearCell( cl.i, cl.j );
if( cl.formula != NIL ) {
addRef( cl.formula, cl.i, cl.j );
cells.put( cl, cl );
changedCells.put( cl, cl );
}
isChanged = true;
}
// does not recalculate dependencies!
public void setWSFormula( int I, int J, String ss ) throws FormulaParseException, EvaluateException {
// evaluate the expression
LispObject rr;
if( ss.length() == 0 )
rr = NIL;
else {
rr = Operator.OPERATOR.parseFormula( ss );
rr = evaluateRef( rr, I, J ); // no ref evaluating required - they already are atoms
}
// LispObject rrr = rr.evaluateSExp();
// no exception occured
// remove old value from dependencies and sheets
Cell oldcell = getCell( I, J );
// Cell cl = new Cell( I,J, rr, rrr, oldcell.format );
Cell cl = new Cell( I,J, rr, oldcell.format );
putCell( cl );
}
// does not recalculate dependencies!
public void setFormula1( int i, int j, String ss ) {
try {
setFormula( i, j, ss );
}
catch( Exception e ) { }
// catch( BadFormulaException e ) {
// System.out.println("system error - exception <" + e.getMessage() + "> wasn't expected here");
// }
}
// evaluates all Refs in the tree
// throws EvaluateException if broken arguments of Reference are specified
static LispObject evaluateRef( LispObject lo, int I, int J ) throws EvaluateException {
if( lo.typeNumber() == TYPE_FUNCTORLIST ) {
FunctorList fl = (FunctorList) lo;
if( fl.functor == Bfunc.BFUNC.table[Bfunc.INDEX_REF] ||
fl.functor == Bfunc.BFUNC.table[Bfunc.INDEX_DATE] ) {
// found, will replace
// argumenst must be constants, so no argument evaluation required
return fromList( fl );
}
final int size = fl.listSize();
if( fl.functor == Bfunc.BFUNC.table[Bfunc.INDEX_QLIST] ) {
final LispObject newargs[] = new LispObject[size-1];
for( int i=1; i<size; i++ ) {
LispObject oldarg = fl.getArgumentN(i);
newargs[i-1] = evaluateRef( oldarg, I, J );
}
return new QuotedList(newargs);
}
final LispObject newargs[] = new LispObject[size];
boolean replace = false;
newargs[0] = fl.functor;
for( int i=1; i<size; i++ ) {
LispObject oldarg = fl.getArgumentN(i);
LispObject newarg = newargs[i] = evaluateRef( oldarg, I, J );
if(oldarg!=newarg)
replace = true;
}
if(replace)
return createFunctorList(newargs);
}
return lo;
}
// and ads it to references
void addRef( LispObject lo, int I, int J ) {
int type = lo.typeNumber();
if( lo instanceof Reference ) {
// found, will replace
Reference ref = (Reference) lo;
// don't add broken refs
if( (ref.absFlags&Reference.BAD_REF) == 0 )
addReference( ref.i1,ref.j1, ref.i2,ref.j2, I,J );
return;
}
if( lo instanceof FunctorList ) {
FunctorList fl = (FunctorList) lo;
final int size = fl.listSize();
for( int i=1; i<size; i++ )
addRef( fl.getArgumentN(i), I, J );
}
if( lo instanceof QuotedList ) {
QuotedList fl = (QuotedList) lo;
for( int i=0; i<fl.value.length; i++ )
addRef( fl.value[i], I, J );
}
}
/*
* Algorithm:
* From every changed cell claculate waves recursevelly with setting step (wave number).
* If come to an already changed cell, increase its step if it's below current one,
* also recursevelly to all depended cells.
* No change if encountered step is above the current one.
* Then calculate cells in order of step (wave number), for whole wave.
*/
Hashtable waveCells; // of WaveCell
Hashtable pathCells; // cell in the path from root change, to control circular references
// canvas is used to notify the caller about repainted cells
void recalculateChangedCells(
//#ifndef NOGUI
CanvasHandler1 canvas
//#endif
) throws CircularReferenceException {
waveCells = createHashtable( changedCells.size() );
pathCells = new Hashtable( 10 );
Enumeration en;
// scann all changed cells which haven't been processed yet
while( (en=changedCells.elements()).hasMoreElements() ) {
Cell cell = (Cell) en.nextElement();
en = null; // we are going to remove cells from changedCells, so enumeration may be broken then
// have to recreate Enumeration
// mark all depended cells
markDepended( cell, 0 );
}
pathCells = null;
// now recalculate cells wave by wave
int level = 0;
while( waveCells.size() > 0 ) {
for( en=waveCells.elements(); en.hasMoreElements(); ) {
WaveCell wc = (WaveCell) en.nextElement();
if( wc.level == level ) {
LispObject rr = FormulaError.ERROR;
debug( "recalculating cell: " + wc.cell.toString() );
try {
rr = wc.cell.formula.evaluateSExp();
}
catch( EvaluateException ee ) {
debug( "error while recalculating cells: " + ee.getMessage() );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -