2016-05-03 10 views
-1

私たちはウェブアプリケーションでVaadinのスプレッドシートコンポーネントを使用しています。 Vaadinのスプレッドシートコンポーネントは、Apache POIを基礎エンジンとして使用します。POIスプレッドシートにライブデータを挿入

私は成功した新しい式のタイプをサポートするために、Vaadinスプレッドシートコンポーネントを拡張しましたApacheのPOIライブラリを使用する:

= njgetdata(ソース、要素、フィルタ)

式は、私たちのvaadinウェブからデータを引き出し返されたデータに基づいてスプレッドシート内で計算を実行することができます。

これまでのところ、とても良いです。

問題は、njgetdataから返される値が時間外(場合によっては1秒に数回)変化することです。

データが変更されるたびに、変更されたデータに依存するセルがあればスプレッドシートを強制的に再計算します。

これを効率的にするには、どのセルがnjgetdata式を使用しているかを特定する必要があると推測しています。また、最新の値を得るために式を再計算する方法もあります。

これを行う方法と効率を上げるための推奨事項を探しています。私はスプレッドシートをスラッシングしたくありません。

+0

Apache POIは式セルの値をキャッシュし、通知するとセルが変更されたときにキャッシュを無効にできます。多分それを試してみませんか? – Gagravarr

+0

したがって、POIにセルが変更されたことをどのように通知しますか?そして、その式が何を意味するのでしょうか? –

+0

[docsのPOI式の評価パフォーマンスノートのセクション](http://poi.apache.org/spreadsheet/eval.html#Performance) – Gagravarr

答えて

1

私はGagravarrの大きなアドバイスといくつかの素晴らしいヒントの後、私はこの問題を解決しました。

ここでは完全に機能する例を示します。

私はUIにライブアップデートを提供するVaadins Spreadsheetコンポーネント用のユーザー定義関数(UDF)を作成したかったのです。

株価のライブアップデートを行い、計算を実行し、結果を表示したいスプレッドシートのケースを考えてみましょう。株価が変わるたびに、計算が自動的に更新されます。

ライブUDFを使用すると、そのようにすることができます。

ですから、例えばあなたがフォームのスプレッドシート式を書くことができますUDFを実装しています:= getprice (「IBM」、「BID」)

getprice UDFはIBMの入札価格たびに返されますそれは変わる。

ここに3つのクラスがあります。 お楽しみください!

package au.com.noojee.dashboard; 

import javax.servlet.annotation.WebServlet; 
import com.vaadin.addon.spreadsheet.Spreadsheet; 
import com.vaadin.annotations.Push; 
import com.vaadin.annotations.Theme; 
import com.vaadin.annotations.VaadinServletConfiguration; 
import com.vaadin.annotations.Widgetset; 
import com.vaadin.server.VaadinRequest; 
import com.vaadin.server.VaadinServlet; 
import com.vaadin.ui.Component; 
import com.vaadin.ui.UI; 
import com.vaadin.ui.VerticalLayout; 

/** 
* This UI is the application entry point. A UI may either represent  a browser 
* window (or tab) or some part of a html page where a Vaadin  application is 
* embedded. 
* <p> 
* The UI is initialized using {@link #init(VaadinRequest)}. This  method is 
* intended to be overridden to add component to the user interface  and 
* initialize non-component functionality. 
*/ 
@Theme("dashboard") 
@Widgetset("au.com.noojee.dashboard.DashboardWidgetset") 
@Push 
public class SpreadsheetUI extends UI 
{ 
    private static final long serialVersionUID = 1L; 
    private Spreadsheet spreadsheet; 

    @WebServlet(urlPatterns = "/*", name = "SpreadsheetUIServlet",  asyncSupported = true) 
    @VaadinServletConfiguration(ui = SpreadsheetUI.class,  productionMode = false) 
    public static class SpreadsheetUIServlet extends VaadinServlet 
    { 
     private static final long serialVersionUID = 1L; 
    } 

    @Override 
    protected void init(VaadinRequest request) 
    { 

     VerticalLayout layout = new VerticalLayout(); 
     this.setContent(layout); 

     layout.setSizeFull(); 

     layout.addComponent(createSpreadsheet()); 
    } 

    // Create the spread sheet and inject the UDF. 
    Component createSpreadsheet() 
    { 
     VerticalLayout layout = new VerticalLayout(); 
     layout.setSizeFull(); 
     spreadsheet = new Spreadsheet(); 
     spreadsheet.setSizeFull(); 

     new DataSource().initFormula(spreadsheet); 
     layout.addComponent(spreadsheet); 

     return layout; 
    } 
} 

package au.com.noojee.dashboard; 

import java.util.HashSet; 
import java.util.Set; 
import java.util.concurrent.Executors; 
import java.util.concurrent.ScheduledExecutorService; 
import java.util.concurrent.TimeUnit; 

import org.apache.poi.ss.formula.functions.FreeRefFunction; 
import org.apache.poi.ss.formula.udf.AggregatingUDFFinder; 
import org.apache.poi.ss.formula.udf.DefaultUDFFinder; 
import org.apache.poi.ss.formula.udf.UDFFinder; 
import org.apache.poi.ss.usermodel.Cell; 
import org.apache.poi.ss.usermodel.CellStyle; 
import org.apache.poi.ss.usermodel.Font; 
import org.apache.poi.ss.usermodel.FormulaEvaluator; 
import org.apache.poi.ss.util.CellReference; 
import org.apache.poi.xssf.usermodel.XSSFFont; 

import com.vaadin.addon.spreadsheet.Spreadsheet; 
import com.vaadin.ui.UI; 

public class DataSource 
{ 
    private static final String UDFNAME = "getstockprice"; 
    private Set<Cell> cellTracker = new HashSet<>(); 
    private GetStockPrice formula = new GetStockPrice(); 

    public void initFormula(Spreadsheet spreadsheet) 
    { 
     String[] functionNames = 
     { UDFNAME }; 
     FreeRefFunction[] functionImpls = 
     { formula }; 

    // Get the UDF finder 
    UDFFinder udfs = new DefaultUDFFinder(functionNames, functionImpls); 
    UDFFinder udfToolpack = new AggregatingUDFFinder(udfs); 

    spreadsheet.getWorkbook().addToolPack(udfToolpack); 

    // We need to track what cells use our UDF so we know which ones 
    // to refresh each time the UDF value changes. 

    spreadsheet.addCellValueChangeListener(event -> { 
     Set<CellReference> cells = event.getChangedCells(); 

     // A cell has just changed. 
     // Lets see if its using our UDF. 
     for (CellReference ref : cells) 
     { 
      Cell cell = spreadsheet.getCell(ref); 
      if (cell.getCellType() == Cell.CELL_TYPE_FORMULA) 
      { 
       // The cell contains a formula so lets see if it contains ours. 
       String formula = cell.getCellFormula(); 
       if (formula.contains(UDFNAME)) 
       { 
        // Yep it contains our formula 
        // so add it to the tracker. 
        System.out.println("adding" + cell); 
        cellTracker.add(cell); 
       } 
      } 
      else 
      { 
       // The cell isn't a formula, but it may have been 
       // previously so lets ensure we remove it from tracking. 
       System.out.println("Removing cell" + cell); 
       if (cellTracker.remove(cell) == true) 
        System.out.println("Removed cell" + cell); 
      } 
      System.out.println(cellTracker.size()); 
     } 

    }); 


    /** 
    * This is not what you want to do!! 
    * 
    * Essentially this is a background thread designed to simulate a price change. 
    * In reality you would have a link to some external data source that provided 
    * pricing. Each time the price changes you would update the cells 
    * that reference that price. 
    * Try to be as selective as possible when choosing the cells to update as 
    * the refresh mechanism is expensive. 
    */ 
    ScheduledExecutorService executor =  Executors.newSingleThreadScheduledExecutor(); 
     executor.scheduleAtFixedRate(new Runnable() 
     { 
      @Override 
      public void run() 
      { 
       UI.getCurrent().access(new Runnable() 
       { 

        @Override 
         public void run() 
        { 
         // simulate a stock price change 
         formula.updatePrice(); 
         System.out.println("refresh"); 

         // refresh all cells that use the stock  price UDF. 
         spreadsheet.refreshCells(cellTracker); 
        } 

       }); 

      } 
     }, 5, 5, TimeUnit.SECONDS); 

    } 
} 


package au.com.noojee.dashboard; 

import java.util.Random; 

import org.apache.poi.ss.formula.OperationEvaluationContext; 
import org.apache.poi.ss.formula.eval.ErrorEval; 
import org.apache.poi.ss.formula.eval.EvaluationException; 
import org.apache.poi.ss.formula.eval.NumberEval; 
import org.apache.poi.ss.formula.eval.OperandResolver; 
import org.apache.poi.ss.formula.eval.ValueEval; 
import org.apache.poi.ss.formula.functions.FreeRefFunction; 

/** 
* 
* Method to simulate retrieving stock prices 
*/ 
public class GetStockPrice implements FreeRefFunction 
{ 

    // select a random starting price. 
    volatile double currentPrice = 10.50; 

    @Override 
    public ValueEval evaluate(ValueEval[] args,  OperationEvaluationContext ec) 
    { 
     double result = 0; 

     if (args.length != 2) 
     { 
      return ErrorEval.VALUE_INVALID; 
     } 

     // The stock code that is being monitored 
     String stockCode; 
     // The price field that is being pulled (e.g. Bid, Last, High, Low etc) 
     String priceField; 

     try 
     { 
      ValueEval v1 = OperandResolver.getSingleValue(args[0],  ec.getRowIndex(), ec.getColumnIndex()); 
      ValueEval v2 = OperandResolver.getSingleValue(args[1],  ec.getRowIndex(), ec.getColumnIndex()); 

      stockCode = OperandResolver.coerceValueToString(v1); 
      priceField = OperandResolver.coerceValueToString(v2); 

      result = currentPrice; 

      checkValue(result); 

     } 
     catch (EvaluationException e) 
     { 
      e.printStackTrace(); 
      return e.getErrorEval(); 
     } 

     return new NumberEval(result); 
    } 

    /** 
    * Excel does not support infinities and NaNs, rather, it gives  a #NUM! 
    * error in these cases 
    * 
    * @throws EvaluationException 
    *    (#NUM!) if <tt>result</tt> is <tt>NaN</> or  <tt>Infinity</tt> 
    */ 
    final void checkValue(double result) throws EvaluationException 
    { 
     if (Double.isNaN(result) || Double.isInfinite(result)) 
     { 
      throw new EvaluationException(ErrorEval.NUM_ERROR); 
     } 
    } 

    public void updatePrice() 
    { 
     this.currentPrice += new Random().nextDouble(); 

    } 

} 
関連する問題