/* * Created on 20 juin 2005 * */ package fr.umlv.table.spread; import java.awt.Component; import java.util.HashMap; import java.util.HashSet; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.script.Bindings; import javax.script.Compilable; import javax.script.CompiledScript; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; import javax.script.SimpleBindings; import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.table.AbstractTableModel; import javax.swing.table.DefaultTableCellRenderer; public class SpreadTheWorld { private static class Cell { final String text; final CompiledScript script; public Cell(String text,CompiledScript script) { this.text=text; this.script=script; } @Override public String toString() { return text; } } private class CellBindings extends SimpleBindings { CellBindings() { // call from the outer class } @Override public boolean containsKey(Object key) { return CELL_REF_PATTERN.matcher(key.toString()).matches() || super.containsKey(key); } @Override public Object get(Object key) { Matcher matcher=CELL_REF_PATTERN.matcher(key.toString()); if (!matcher.matches()) return super.get(key); Cell cell=getCell(Integer.parseInt(matcher.group(2)), getColumnIndexFromName(matcher.group(1))); if (cell==null) return 0; return eval(cell); } } static final Pattern CELL_REF_PATTERN=Pattern.compile("(\\p{Alpha}+)(\\p{Digit}+)"); private class ExCellTableModel extends AbstractTableModel { ExCellTableModel() { // call from the outer class } public int getRowCount() { return 1000; } public int getColumnCount() { return 100; } public Object getValueAt(int row, int column) { return getCell(row,column); } @Override public boolean isCellEditable(int row, int column) { return true; } @Override public void setValueAt(Object aValue, int row, int column) { String text=aValue.toString(); CompiledScript script=null; try { script = compilable.compile(text); } catch (ScriptException e) { e.printStackTrace(); return; } setCell(row,column,text,script); // not efficient !!! fireTableDataChanged(); } } //swing is single-threaded !! private transient HashSet circularity= new HashSet(); private final Bindings bindings=new CellBindings(); final Compilable compilable; public SpreadTheWorld(ScriptEngine engine) { this.compilable=(Compilable)engine; } Object eval(Cell cell) { if (cell.script==null) return cell.text; if (!circularity.add(cell)) { throw new IllegalStateException("circular refs"); } try { return cell.script.eval(bindings); } catch (ScriptException e) { return e; } finally { circularity.remove(cell); } } Cell getCell(int row,int column) { return cellMap.get(mangle(row,column)); } void setCell(int row,int column,String text,CompiledScript script) { cellMap.put(mangle(row,column),new Cell(text,script)); } private static long mangle(int row,int column) { return (((long)row)<<32)+column; } private final HashMap cellMap= new HashMap(); static int getColumnIndexFromName(String columnName) { int value=0; for(int i=0;i