わいえむねっと

Contents
Categories
Calendar
2026/01
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Monthly Archives
~2000/01
Recent Entries
RSS1.0
Templates
Information
Processed: 0.053 sec
Chashed: -
2019/02/07 Thu

前回までのあらすじ

Apache POI が共有数式の作成に対応していないっポイ!(渾身のギャグ)(※1)


※1)今回調査したバージョンは3.9だけど、最新の4.0.1でも未対応の模様。



というわけで、Open XML ファイルフォーマットのオブジェクトを直接いじってどうにかしてみる。

まずは適当に数式が共有されそうなデータをExcelでつくって中を覗いてみる。



  • A~B列に数値を入力
  • C1 に "=A1*B1" を入力
  • C2:C3 に C1 をコピー


xl/worksheets/sheet1.xml
  <sheetData>
    <row r="1" spans="1:3" x14ac:dyDescent="0.4">
      <c r="A1">
        <v>1</v>
      </c>
      <c r="B1">
        <v>4</v>
      </c>
      <c r="C1">
        <f>A1*B1</f>
        <v>4</v>
      </c>
    </row>
    <row r="2" spans="1:3" x14ac:dyDescent="0.4">
      <c r="A2">
        <v>2</v>
      </c>
      <c r="B2">
        <v>5</v>
      </c>
      <c r="C2">
        <f t="shared" ref="C2:C3" si="0">A2*B2</f>
        <v>10</v>
      </c>
    </row>
    <row r="3" spans="1:3" x14ac:dyDescent="0.4">
      <c r="A3">
        <v>3</v>
      </c>
      <c r="B3">
        <v>6</v>
      </c>
      <c r="C3">
        <f t="shared" si="0" />
        <v>18</v>
      </c>
    </row>
  </sheetData>

C2とC3が共有数式になってますね。
この場合、C1は通常セルになっているけど、C1を共有元にすることも可能かな。



Excelの出力結果を参考に値をセット。

  • cell は XSSFCell のインスタンス
  • 値si は共有数式のインデックスなので式の数に合わせて採番
  • 要素v は計算したら設定される(ハズ)なので省略

共有元

        CTCellFormula f = CTCellFormula.Factory.newInstance();
        f.setT(STCellFormulaType.SHARED);
        f.setRef("C1:C3");
        f.setSi(0);
        f.setStringValue("A1*B1");

        CTCell c = cell.getCTCell();
        c.setF(f);

共有先

        CTCellFormula f = CTCellFormula.Factory.newInstance();
        f.setT(STCellFormulaType.SHARED);
        f.setSi(0);

        CTCell c = cell.getCTCell();
        c.setF(f);


実行してみたところ、計算でエラー。

java.lang.IllegalStateException: Master cell of a shared formula with sid=0 was not found
    at org.apache.poi.xssf.usermodel.XSSFCell.convertSharedFormula(XSSFCell.java:383)
    at org.apache.poi.xssf.usermodel.XSSFCell.getCellFormula(XSSFCell.java:368)
    at org.apache.poi.xssf.usermodel.XSSFEvaluationWorkbook.getFormulaTokens(XSSFEvaluationWorkbook.java:148)
    at org.apache.poi.ss.formula.WorkbookEvaluator.evaluateAny(WorkbookEvaluator.java:286)
    at org.apache.poi.ss.formula.WorkbookEvaluator.evaluate(WorkbookEvaluator.java:230)
    at org.apache.poi.xssf.usermodel.XSSFFormulaEvaluator.evaluateFormulaCellValue(XSSFFormulaEvaluator.java:264)
    at org.apache.poi.xssf.usermodel.XSSFFormulaEvaluator.evaluateFormulaCell(XSSFFormulaEvaluator.java:151)
    at org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.evaluateAllFormulaCells(HSSFFormulaEvaluator.java:327)
    at org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.evaluateAllFormulaCells(HSSFFormulaEvaluator.java:318)
    at org.apache.poi.xssf.usermodel.XSSFFormulaEvaluator.evaluateAllFormulaCells(XSSFFormulaEvaluator.java:238)

XSSFSheet の sharedFormulas に該当がないと怒られている。

org/apache/poi/xssf/usermodel/XSSFSheet.java
    /**
     * cache of master shared formulas in this sheet.
     * Master shared formula is the first formula in a group of shared formulas is saved in the f element.
     */
    private Map<Integer, CTCellFormula> sharedFormulas;
    private TreeMap<String,XSSFTable> tables;
    private List<CellRangeAddress> arrayFormulas;
    private XSSFDataValidationHelper dataValidationHelper;    



リフレクションでねじ込む。

  • sheet は XSSFSheet のインスタンス

        Field field = sheet.getClass().getDeclaredField("sharedFormulas");
        field.setAccessible(true);
        Map<Integer, CTCellFormula> sharedFormulas = (Map<Integer, CTCellFormula>) field.get(sheet);
        sharedFormulas.put(0, f);

再度実行してみたところ正常終了。
ファイルを開いてみたところ、計算結果も表示されました。

いけるじゃーん。


CTCell#setF を呼んでいる回数は 行×列 のままなので、パフォーマンス的には大差ないか?と思ったけど、大幅に向上。
CTCellFormula に数式が含まれている場合にコストがかかるんだな。



はまった点とか

  • CTCell#setF に渡した CTCellFormula のインスタンスを更新しても、CTCell には反映されない。再セットが必要。
  • shiftRows で行をずらすと、セル本体の ref と数式は更新されるが、sharedFormulas 側は更新されない。
いつものラム肉がない! 注文していた電源が到着。
予備電源を使い続けてもよかったのだけど、故障した剛力短2がサイズ小さくてよかったので後継機を購入。


剛短4 プラグイン 500W・600W・700W(ごうたん4) | 株式会社サイズ
https://www.scythe​.co.jp/product/power/spgt4
2019/02/06 Wed
階下が退去してた。

一般入居者ではなく会社事務所だったから夜間と休日は人がいなくて(※1)、夜中に酔っ払って盛大に転んでもぜんぜん気にしなくて大丈夫(※2)だったのに。


※1)そもそも平日の日中も人の出入りを見たことないけど。
※2)なにも大丈夫ではない。 考え事しながらビル内を歩いていたら向こうから見たことあるような人がきたなと思ったら声をかけられる。

前の現場でいっしょだった人でした。

ぐうぜーん。 処理に時間がかかりすぎてタイムアウトするやつ、蓋を開けたら Apache POI だったのでコードを眺める。




ボトルネックになっているのは XSSFCell#setCellFormula。

org/apache/poi/xssf/usermodel/XSSFCell.java
    /**
     * Sets formula for this cell.
     * <p>
     * Note, this method only sets the formula string and does not calculate the formula value.
     * To set the precalculated value use {@link #setCellValue(double)} or {@link #setCellValue(String)}
     * </p>
     *
     * @param formula the formula to set, e.g. <code>"SUM(C4:E4)"</code>.
     *  If the argument is <code>null</code> then the current formula is removed.
     * @throws org.apache.poi_v3_9.ss.formula.FormulaParseException if the formula has incorrect syntax or is otherwise invalid
     * @throws IllegalStateException if the operation is not allowed, for example,
     *  when the cell is a part of a multi-cell array formula
     */
    public void setCellFormula(String formula) {
        if(isPartOfArrayFormulaGroup()){
            notifyArrayFormulaChanging();
        }
        setFormula(formula, FormulaType.CELL);
    }

    private void setFormula(String formula, int formulaType) {
        XSSFWorkbook wb = _row.getSheet().getWorkbook();
        if (formula == null) {
            wb.onDeleteFormula(this);
            if(_cell.isSetF()) _cell.unsetF();
            return;
        }

        XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
        //validate through the FormulaParser
        FormulaParser.parse(formula, fpb, formulaType, wb.getSheetIndex(getSheet()));

        CTCellFormula f = CTCellFormula.Factory.newInstance();
        f.setStringValue(formula);
        _cell.setF(f);
        if(_cell.isSetV()) _cell.unsetV();
    }

バリデートのコストが高いのか?と、ダイレクトに CTCell#setF するようにしてみたけど大して変わらず。
openxmlformats の中だと手がだせんな…



なにか別のアプローチはないものかと眺めたところ、shared formula とかいうのが。
数式を共有する構造があるのか。

org/apache/poi/xssf/usermodel/XSSFSheet.java
    /**
     * cache of master shared formulas in this sheet.
     * Master shared formula is the first formula in a group of shared formulas is saved in the f element.
     */
    private Map<Integer, CTCellFormula> sharedFormulas;
    private TreeMap<String,XSSFTable> tables;
    private List<CellRangeAddress> arrayFormulas;
    private XSSFDataValidationHelper dataValidationHelper;    

    void onReadCell(XSSFCell cell){
        //collect cells holding shared formulas
        CTCell ct = cell.getCTCell();
        CTCellFormula f = ct.getF();
        if (f != null && f.getT() == STCellFormulaType.SHARED && f.isSetRef() && f.getStringValue() != null) {
            // save a detached  copy to avoid XmlValueDisconnectedException,
            // this may happen when the master cell of a shared formula is changed
            CTCellFormula sf = (CTCellFormula)f.copy();
            CellRangeAddress sfRef = CellRangeAddress.valueOf(sf.getRef());
            CellReference cellRef = new CellReference(cell);
            // If the shared formula range preceeds the master cell then the preceding  part is discarded, e.g.
            // if the cell is E60 and the shared formula range is C60:M85 then the effective range is E60:M85
            // see more details in https://issues.apache.org/bugzilla/show_bug.cgi?id=51710
            if(cellRef.getCol() > sfRef.getFirstColumn() || cellRef.getRow() > sfRef.getFirstRow()){
                String effectiveRef = new CellRangeAddress(
                        Math.max(cellRef.getRow(), sfRef.getFirstRow()), sfRef.getLastRow(),
                        Math.max(cellRef.getCol(), sfRef.getFirstColumn()), sfRef.getLastColumn()).formatAsString();
                sf.setRef(effectiveRef);
            }

            sharedFormulas.put((int)f.getSi(), sf);
        }
        if (f != null && f.getT() == STCellFormulaType.ARRAY && f.getRef() != null) {
            arrayFormulas.add(CellRangeAddress.valueOf(f.getRef()));
        }
    }

org/apache/poi/xssf/usermodel/XSSFCell.java
    /**
     * Return a formula for the cell, for example, <code>SUM(C4:E4)</code>
     *
     * @return a formula for the cell
     * @throws IllegalStateException if the cell type returned by {@link #getCellType()} is not CELL_TYPE_FORMULA
     */
    public String getCellFormula() {
        int cellType = getCellType();
        if(cellType != CELL_TYPE_FORMULA) throw typeMismatch(CELL_TYPE_FORMULA, cellType, false);

        CTCellFormula f = _cell.getF();
        if (isPartOfArrayFormulaGroup() && f == null) {
            XSSFCell cell = getSheet().getFirstCellInArrayFormula(this);
            return cell.getCellFormula();
        }
        if (f.getT() == STCellFormulaType.SHARED) {
            return convertSharedFormula((int)f.getSi());
        }
        return f.getStringValue();
    }

でも、CTCellFormula#setT で STCellFormulaType.SHARED を設定しているコードや、CTCellFormula#setSi でインデックスを設定しているコードが見当たらない。
読み取りにしか対応していない?

読み取ったものは書き出しできるはずだから、なんとかできないこともないと思うけど。



なにか他にないかとさらに探したところ、

org/apache/poi/xssf/usermodel/XSSFSheet.java
    public CellRange<XSSFCell> setArrayFormula(String formula, CellRangeAddress range) {

        CellRange<XSSFCell> cr = getCellRange(range);

        XSSFCell mainArrayFormulaCell = cr.getTopLeftCell();
        mainArrayFormulaCell.setCellArrayFormula(formula, range);
        arrayFormulas.add(range);
        return cr;
    }

配列数式。
いわゆるCSE数式。そんなものあったな!

これなら、数式を設定する数が 列×行 ⇒ 列 にできる。
複雑な数式には対応できないけど。

今回の内容であれば適用可能なので試しに実装してみたところ、速度的には問題なし。
とりあえず候補だな。 いつも大体しめじなので、たまにはまいたけ。 レトロな両手鍋が欲しいなーと比較検討してポチッていたものが到着。

以下、候補。




和平フレイズ - Wikipedia
https://ja.wikiped​ia.org/wiki/%E5%92%8C%​E5%B9%B3%E​3%83%95%E3​%83%AC%E3%​82%A4%E3%8​2%BA
和平フレイズ株式会社(わへいフレイズ)とは、新潟県燕市に本社を置く日本の企業である。主にフライパンや鍋などのキッチン用品を企画・販売している。主な商品に平野レミのレミパンや有元葉子のラバーゼシリーズがある。

レミパンのとこ。



パール金属 - Wikipedia
https://ja.wikiped​ia.org/wiki/%E3%83%91%​E3%83%BC%E​3%83%AB%E9​%87%91%E5%​B1%9E
パール金属株式会社(パールきんぞく)とは、新潟県三条市に本社を置く日本の企業である。主にフライパンや圧力鍋などの金属製台所用品などを製造している。また、アウトドア用品を製造・販売しているキャプテンスタッグ (CAPTAIN STAG) のブランドでも知られている。

キャプテンスタッグのとこ。





和平フレイズは「薄い」、パール金属は「テープがベタベタする」てレビューが目立っていたので敬遠。
北陸アルミはレビュー数が一番少ないんだけど、そこは値段と反比例なのかなーと。
というか、比較すると一番高いけど、そもそも別に高くないよね。

というわけで、北陸アルミのものを購入したのでした。

結果、他二社のレビューにでていたマイナス要素はまったくないですね。いい感じ。
2019/02/05 Tue
首になんかしこりが。 ちょっとデータを増やすと経路の途中で噛んでいるサーバーがタイムアウトしてしまってどうにもならんみたいな状態だったので調べてみたところサードパーティーのライブラリがボトルネックになっていたわけですがこのライブラリが使えないとなるとここの出力を一から作り直しみたいな話になるので見なかったことにして定時で帰る。
2019/02/04 Mon
2週間くらい体調不良の状態が続いていたけどようやく復調した感。
ここ数日はよく寝てたしな!(※1)


※1)酔っぱらって寝こけていただけなので、健康という面ではむしろよろしくない。
【レビュー】よっちゃんイカ専用の日本酒!?「春鶯囀 カットよっちゃん専用日本酒」を飲んでみた | NOMOOO(ノモー) - 日本最大級のお酒専門メディア
https://www.nomooo​.jp/column/120398/

試しに買ってみるか。
次は何飲む? ~カクテルを検索するWebアプリを作ってみた~ - Qiita
https://qiita.com/kon2/items/ba97666080​2a9600a38b​

おもしろいし使えそう。

なお、自分はよく「スコッチベースでなにかー」と頼むので、"スコッチ"で検索してみたけど引っ掛からないなーと思ったら、

× スコッチ
× スコッチウィスキー
○ スコッチウイスキー

でした。 Facebook経由で日記にアクセスがあって何かと思ったら、これこれ
十徳…?
2019/02/03 Sun
日本酒6合飲んで帰ってきて寝て起きたら5時。

ここんとこなんなん。 じゃがアリゴつくってみたけど別に激ウマではないな。ネット飯ですね。
酒のアテになるってのも見たけど、元のままのが酒のアテになりますわ。


ボトルを手に取って選べるのイイですね。
味の感想がタグで付いてるのも語彙力が死んでる勢にはありがたい。

まぁ語彙力死んでるから、この場合の感想は「タグに書いてあった通りだ~!」になりますけどね。
2019/02/02 Sat
日本酒4合飲んで帰ってきて、ストロング1.5リットル飲んで、寝て、目覚ましでいったん起きて、二度寝したら昼でした。

もえるごみだしそこねた。 久し振りにかよちゃんにいってみる。

総菜の盛り合わせを注文してから「日本酒お願いしまーす」と後ろのテーブルに取りに行ったら、

女将「あら、お客さんきたことある?」
なや「はい、2回目ですー」
女将「前きた時、ここに座ってた…?」
なや「あ、はい!そうですそうです」

さすが客商売。よく覚えてらっしゃる。

なや「隣がご夫婦に娘さんで、後ろにミュージシャンの方がいて…」
女将「あら、それって(自分の隣にいた人の方に手をやる」
なや「えっ」

今日もいたー!

なや「前回は12月の頭だったから2ヶ月ぶりですねー」
ミュ「あれから2ヶ月も経ってましたっけ?!」

こちらにも覚えられてたw


かよちゃん - 鬼越/居酒屋 [食べログ]
https://tabelog.co​m/chiba/A1202/A120202/12020652/





まずは総菜の盛り合わせ。

  • 栄川 特醸酒
  • 2杯いただく。




前回食べられなかった栃尾揚げ。
薬味たっぷりで良かったですねぇ。
写真だと分かりにくいけど、サクラエビが乗っているのがこれまたイイ。

  • 会津ほまれ 清酒
  • こちらも2杯いただく。




サバ君。
鯖の燻製ですね。めっちゃ酒のアテになる。

  • 木戸泉 本醸
  • また2杯いただく。
  • 「ちゃんと水も飲んでね!」て言われる。
2019/02/01 Fri
ストロング飲んでワイン飲んでスコッチをストレートで飲んで焼酎をロックで飲んで寝て起きたら4時。
おはようございます。2月です。
新宿「日本再生酒場」若き立ち飲み、いまや街の立派な景色
https://syupo.com/archives/23337
昭和をコンセプトにした、とあえて書きましたが、実はそのルーツは1950年(昭和25年)に中野で創業の4席しかない立ち飲み屋に遡れます。1974年(昭和49年)に調布へ移転し、以来調布の人々に愛されている「い志井」です。

日本再生酒場て い志井の系列だったのかー。
盛岡五郎とも姉妹店なんですね。こっちでも つくPだしてる模様。

コンセプト居酒屋感が強くて敬遠してたけど、いっぺんいってみるか。
十徳ポイントカードのご案内:酒処十徳
http://www.juttok​u.com/backnumber​/bn070508.h​tm
十徳グループでは全店共通でご利用頂ける「十徳ポイントカード」をスタートいたします。

十徳でポイントカードはじまったの?!まーじで。

■30ポイント:3,000円分のご飲食券+十徳オリジナルグッズ

オリジナルグッズは気になるけど、30回はなかなか厳しいなー。
月一の利用で2年以上かかりますやん。 今日は飲んで帰るぞジョジョーッ!

前回いけなかった俵やに再チャレンジしてみたけど今回もいっぱい。
次に小伝馬にいってみたけどこちらもいっぱい。ぐぬぬ。
と思いきや、

店員さん「こちらでよければ…」

ほぼ通路のカウンター席にねじ込んでもらえました。やったぜ!
超狭くて店員さんが通るたびにがんがんあたってたけど!(※1)


小伝馬 - 馬喰横山/居酒屋 [食べログ]
https://tabelog.co​m/tokyo/A1302/A130204/13102226/


※1)ねじ込んでもらえただけでありがたかったので文句は一切ない。



ここは道灌を各種取り扱っているんですよねー。飲み比べ。




貝の盛り合わせ。ホッキ貝/ツブ貝/トリ貝。

  • 道灌 山廃純米




サクラマスの塩焼き。めちゃくちゃうまかった。

  • 千代田蔵 特別純米生原酒 山廃
  • 道灌 本醸造生原酒 蔵出し一番酒




栃尾揚げ。

  • 道灌 清酒
  • 燗で。
2019/01/31 Thu
サッポロ マグナム限定発売(2019年1月30日) | ニュースリリース | 会社情報 | サッポロビール
http://www.sappor​obeer.jp/news_relea​se/0000022101​/index.html​
RTD市場は2018年も大幅な伸長を続けており、中でもアルコール度数7~9%のストロング系が市場の伸長をけん引しています。背景としてRTDにコストパフォーマンスを求める消費者ニーズの高まりがあり、その需要に応えるべくさらに高アルコールの商品を発売します。

当社は、「アルコール12%」かつ「強炭酸」の独自価値をもつRTDを発売することで伸長する市場の活性化に貢献します。

サッポロからも12%がでるのかー。
マグナム!



そもそもRTDてナニと思ったら、 ready to drink の略で「開けてすぐ飲める飲料」だとか。
対して、「開けてグラスに注ぐだけで飲める飲料」が ready to serve でRTS。リアルタイムストラテジーじゃない。 おなか痛くて4時くらいまで寝付けずにいたけど下したらすっきりしたので寝る。
欲しい!
2019/01/30 Wed
レスポンスの計測をしようとしたけど昼から開始されるウィルススキャンが一向に終わらずCPUが占有され続けているので、これじゃ計測できねーよとPCを再起動したら Windows Update が始まって白目。
しかも大型アップデート(Fall Creators)。クソがー!2時間とかかかった。

しかも終わったら終わったでExcelのカーソルがぷるぷるするようになったし。

Windows 10 Fall Creators Update (v1709) に更新した後、Excel のカーソルがちらつく - Excel
https://support.of​fice.com/ja-jp/article/Windows-10-Fall-Creators-Update-v1709-%E3%81%AB%​E6%9B%B4%E​6%96%B0%E3​%81%97%E3%​81%9F%E5%B​E%8C%E3%80​%81Excel-%E3%81%AE%​E3%82%AB%E​3%83%BC%E3​%82%BD%E3%​83%AB%E3%8​1%8C%E3%81​%A1%E3%82%​89%E3%81%A​4%E3%81%8F​-94b30c81-52eb-4008-b932-8f2e6aa03d​1b?ui=ja-JP&rs=ja-JP&ad=JP
Windows 10 Fall Creators Update に更新した後、Excel ブックを開いたり、ブックのセルに移動したりしたときに、一部のデバイスでカーソルのちらつきが発生する場合があります。

最新の Office 更新プログラムをインストールします。これを行うには、いずれかの Office アプリを開き、[ファイル]、[アカウント]、[更新オプション]、[今すぐ更新] の順に選びます。

「今すぐ更新」しても「最新の状態です」としか言われないし。クソがーー! readyイベントで処理しているXMLHTTPRequestが遅い。IEでだけ。
通常20~40msくらいなのに、1s以上かかったりしている。

ChromeはもちろんEdgeでもそんなことはなく、IEでも表示後のイベントであれば遅くない。
なんでだ。
IEぶん投げたい。
2019/01/29 Tue
テスト環境のメンテナンス日でアクセスが禁止されていたのに、モジュールリリースするわ、JMeterで負荷かけまくるわとやりたい放題やってるやつがいたんですよね。

僕なんですけどね。 なんかうんこが緑色なんですが、キャベツの食べ過ぎ以外に心当たりがない。