tag:crieit.net,2005:https://crieit.net/tags/Java/feed 「Java」の記事 - Crieit Crieitでタグ「Java」に投稿された最近の記事 2022-07-17T13:24:40+09:00 https://crieit.net/tags/Java/feed tag:crieit.net,2005:PublicArticle/18245 2022-07-17T13:24:18+09:00 2022-07-17T13:24:40+09:00 https://crieit.net/posts/mal-lisp-template-engine malでかんたんなテンプレートエンジンを書いてみた <p>これは <a target="_blank" rel="nofollow noopener" href="https://qiita.com/advent-calendar/2021/lisp">Lisp Advent Calendar 2021</a> の25日目の記事です。Qiita に書いていた記事のクロスポストです。</p> <hr /> <p>言語非依存なテンプレートエンジンがあったらいいなと昔からボンヤリ考えていた(切実にほしいという程ではない)のですが、<a target="_blank" rel="nofollow noopener" href="https://github.com/kanaka/mal">mal(Make a Lisp)</a>で作ったらどうだろうと思って試しにやってみました。「なんかやってみたくなってやってみた」系の記事です。</p> <p>Lisp を書くのは年に数回という感じの人が書いていますので、拙いところは大目に見ていただければと思います。</p> <p>↓ちなみに去年のアドベントカレンダーではこんなのを書きました。</p> <p><a target="_blank" rel="nofollow noopener" href="https://qiita.com/sonota88/items/501f0efa437bc4d53cc7">LibreOffice BasicでLispインタプリタ(mal)を書いた - Qiita</a></p> <h1 id="できたもの"><a href="#%E3%81%A7%E3%81%8D%E3%81%9F%E3%82%82%E3%81%AE">できたもの</a></h1> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/sonota88/malten">https://github.com/sonota88/malten</a></p> <p><code>sonota88/mal</code> をサブモジュールとして使っているので、 <code>git clone --recursive ...</code> でクローンする必要があります。</p> <h1 id="Ruby版"><a href="#Ruby%E7%89%88">Ruby版</a></h1> <p>Ruby 版だとこんな感じ。</p> <pre><code class="ruby">template = <<TEMPLATE <h1>品目一覧</h1> <p>購入日: <%= date %></p> <table> <tr> <th>ID</th> <th>品名</th> <th>価格</th> </tr> <% (map (fn* [item] (do %> <tr> <td><%= (get item "id") %></td> <td><%= (get item "name") %></td> <td><%= (- (get item "price") (if (get item "discount") (get item "discount") 0)) %> 円</td> </tr> <% )) items) %> </table> TEMPLATE context = { date: "2021-12-25", items: [ { id: 1, name: "foo", price: 100, discount: nil }, { id: 2, name: "bar", price: 200, discount: nil }, { id: 3, name: "baz", price: 300, discount: 50 } ] } rendered = Malten.render(context, template) print rendered </code></pre> <p><code><% ... %></code> または <code><%= ... %></code> の中にコードを書きます。ERB や JSP に倣ってベタなスタイルにしましたが、コードの部分が mal なので、見たことあるような、ないような、怪しげな雰囲気ですね。</p> <hr /> <p>出力も一応貼っておきます。</p> <pre><code class="html"><br /><h1>品目一覧</h1> <p>購入日: 2021-12-25</p> <table> <tr> <th>ID</th> <th>品名</th> <th>価格</th> </tr> <tr> <td>1</td> <td>foo</td> <td>100 円</td> </tr> <tr> <td>2</td> <td>bar</td> <td>200 円</td> </tr> <tr> <td>3</td> <td>baz</td> <td>250 円</td> </tr> </table> </code></pre> <h1 id="Java版"><a href="#Java%E7%89%88">Java版</a></h1> <p>Java 版も作ってみました(テンプレートは同じなので省略)。</p> <pre><code class="java"> String template = getTemplate(); Context context = new Context(); context.map.put("date", "2021-12-25"); { List<Map<String, Object>> items = new ArrayList<>(); items.add(new Item(1, "foo", 100, null).toPlain()); items.add(new Item(2, "bar", 200, null).toPlain()); items.add(new Item(3, "baz", 300, 50).toPlain()); context.map.put("items", items); } String rendered = Malten.render(context, template); System.out.println(rendered); </code></pre> <p>大体似たような感じですね。</p> <h1 id="概観"><a href="#%E6%A6%82%E8%A6%B3">概観</a></h1> <p>大雑把な処理の流れ。</p> <pre><code>テンプレートテキスト ↓ ↓レンダリング用コード生成 ↓ レンダリング用コード(mal のコード) ↓ ↓eval(ここでコンテキストを参照) ↓ 出力テキスト </code></pre> <h1 id="レンダリング用コード生成"><a href="#%E3%83%AC%E3%83%B3%E3%83%80%E3%83%AA%E3%83%B3%E3%82%B0%E7%94%A8%E3%82%B3%E3%83%BC%E3%83%89%E7%94%9F%E6%88%90">レンダリング用コード生成</a></h1> <p>たとえば、次のようなテンプレートテキストがあったとして、</p> <pre><code class="erb"># 品目一覧 <% (map (fn* [item] (do %> - <% (print (get item "name")) %> <% (print (get item "price")) %> 円 <% )) items) %> </code></pre> <p>これを変換すると次のような mal のコードになる。元のテンプレートと比べて眺めると何やってるかなんとなく分かりますよね。</p> <pre><code class="clojure">(print "# 品目一覧\n\n") (map (fn* [item] (do (print "\n\n- ") (print (get item "name")) (print " ") (print (get item "price")) (print " 円\n\n") )) items) </code></pre> <p>下請けの関数などは除いて骨組部分だけ貼ります。</p> <pre><code class="clojure">;; malten.mal ;; <% ... %> の中身の長さを求める (def! mal-code-length (fn* [rest] (let* [iter (fn* [rest2 pos] (if (s#start-with? rest2 "%>") pos (iter (s#rest rest2) (+ pos 1)))) ] (iter rest 0)))) ;; <% ... %> の部分の処理 (def! gen-renderer-code (fn* [rest buf acc] (let* [len (mal-code-length rest)] (gen-renderer-text (s#drop rest (+ len 2)) "" ; clear buf (cons (if (s#start-with? rest "=") (list 'code-print (s#substring rest 1 len)) (list 'code (s#substring rest 0 len))) (cons (list 'text buf) acc)))))) (def! gen-renderer-text (fn* [rest buf acc] (if (nil? rest) (cons (list 'text buf) acc) ; end of iteration (if (s#start-with? rest "<%") (gen-renderer-code (s#drop rest 2) ; "<%" を除去 buf acc) (gen-renderer-text (s#drop rest 1) (str buf (s#first rest)) acc))))) (def! to-code (fn* [part] (let* [ type (nth part 0) content (nth part 1) ] (cond (= type 'code) content (= type 'code-print) (str "(print " content ")\n") (= type 'text) (str "(print " (pr-str content) ")\n"))))) (def! gen-renderer (fn* [template] (let* [reversed-parts (gen-renderer-text template "" ; buf '() ; acc ) ] (l.foldr (fn* [part acc] (str acc (to-code part))) "" reversed-parts)))) </code></pre> <p>軽い用途向けでも、たとえば入力が 1000 文字を越えると動きません、では困るので TCO(末尾呼び出しの最適化)が効くように書く必要があります。理解があやふやだったのですが、今回 mal の TCO のしくみについておさらいして、ある程度書いて慣れることができて良かったです。</p> <h1 id="レンダリング用コードの eval"><a href="#%E3%83%AC%E3%83%B3%E3%83%80%E3%83%AA%E3%83%B3%E3%82%B0%E7%94%A8%E3%82%B3%E3%83%BC%E3%83%89%E3%81%AE+eval">レンダリング用コードの eval</a></h1> <p>基本的には eval するだけですが、コンテキストは mal の型に合わせて変換してあげる必要があります。Ruby 版だとこんな感じ。</p> <pre><code class="ruby"># ruby/mal.rb def self.to_mal_val(v) case v when Array # List は mal 側で用意されているクラス List.new(v.map { |el| to_mal_val(el) }) when Hash v .to_a .map { |k, _v| [k.to_s, to_mal_val(_v)] # Java版に合わせてキーを String にしている } .to_h else v end end </code></pre> <h1 id="core への追加"><a href="#core+%E3%81%B8%E3%81%AE%E8%BF%BD%E5%8A%A0">core への追加</a></h1> <p>文字列処理をするための最低限の関数として <code>s#first</code>, <code>s#rest</code>, <code>s.cons</code> と、改行なしで print する <code>print</code> を追加しました。</p> <p>Ruby 版であればこんなの。</p> <pre><code class="ruby"># ruby/mal.rb ADDITIONAL_CORE_FUNCS = { :print => lambda { |x| print x }, :"s#first" => lambda { |_self| _self[0] }, :"s#rest" => lambda { |_self| if _self.size <= 1 nil else _self[1..-1] end }, :"s.cons" => lambda { |first, rest| if rest.nil? first else first + rest end } } </code></pre> <p>あとはこれを <code>$core_ns</code> に追加してあげればOK。</p> <pre><code class="ruby"># {mal}/impls/ruby/stepA_mal.rb のこの部分 $core_ns .merge(ADDITIONAL_CORE_FUNCS) </code></pre> <p>他にも便利な関数を追加してリッチにしていくと、mal のレイヤーで書く部分が楽になったりパフォーマンスが改善されたりすると思うのですが、移植コストとのトレードオフになるでしょう。</p> <hr /> <p>というわけで、 mal のコードを一度書くだけ(※1)で 86 の言語(※2)で同じように動く(※3)テンプレートエンジンが手に入りました。</p> <p>※1: 実際は各言語ごとに多少手を入れる必要あり<br /> ※2: 2021-12-25 現在<br /> ※3: たぶん。試してないです。</p> <h1 id="メモ"><a href="#%E3%83%A1%E3%83%A2">メモ</a></h1> <ul> <li>動いたので満足(という程度の試みです)</li> <li>2日くらいで書いたプロトタイピングです。細かいとこは雑。</li> <li>自分用のツールなどでは様子を見つつ使っていこうかなという気持ちになった</li> <li>テンプレートエンジン、ミニマムな機能だけでよければ簡単。「試しに何か書いてみたい」というときのお題としてもお手頃で、実用できそうな雰囲気があるのも良いです。</li> <li><code>pr-str</code> は言語依存の機能で実装されている <ul> <li>たとえば Ruby 版は inspect、 Java 版では commons-lang3 の StringEscapeUtils を使っている</li> <li>コーナーケースが気になる場合は mal で書き直すとよさそう</li> </ul></li> <li>遅い。文字列の処理で、一度文字のリストにばらして加工してまた文字列に戻す(<code>s#to-chars</code> と <code>s.from-chars</code>)ということをやっていて、遅いです。<br /> 文字列版の car, cdr, cons だけあればあとはリスト用の関数に丸投げできるよね、というのを(やったことなかったので)試してみたかったのでした。今回やってみて気が済みました。<br /> 直交性はあるけど普通に計算量的に厳しい、という体験ができました。後で書き直すかも。</li> <li>たとえば Java でテンプレートエンジン自作するとなると、コード生成まではいいとして実行どうするんだろ、コンパイルしないといけないよね? <a target="_blank" rel="nofollow noopener" href="https://www.ne.jp/asahi/hishidama/home/tech/java/JavaCompiler.html">JavaCompiler</a><br /> を使えばできそう? とかのあたりがめんどくさそうだなーと思って実際に作るとこまで至っていなかったのですが(やったことないので億劫)、Java で書かれたインタプリタで動かせばコンパイルのことを考えなくてよくなると気付いて、そっか、そういう手があるのか、なるほどとなりました。で、そういうのを思いついたときにサッと使える mal は便利。</li> </ul> <h2 id="Java版だとhashmapのキーとしてシンボルが使えない"><a href="#Java%E7%89%88%E3%81%A0%E3%81%A8hashmap%E3%81%AE%E3%82%AD%E3%83%BC%E3%81%A8%E3%81%97%E3%81%A6%E3%82%B7%E3%83%B3%E3%83%9C%E3%83%AB%E3%81%8C%E4%BD%BF%E3%81%88%E3%81%AA%E3%81%84">Java版だとhashmapのキーとしてシンボルが使えない</a></h2> <p>詳しく調べてませんがメモ。</p> <p>Java版は hashmap のキーとして文字列を期待しているらしく、エラーになります。</p> <pre><code>Mal [java] user> { 'a 123 } Uncaught java.lang.ClassCastException: mal.types$MalList cannot be cast to mal.types$MalString: mal.types$MalList cannot be cast to mal.types$MalString # 文字列ならOK user> { "a" 123 } {"a" 123} </code></pre> <p>Ruby 版だとシンボルでもOK。</p> <pre><code>Mal [ruby] user> { 'a 123 } {a 123} </code></pre> sonota486 tag:crieit.net,2005:PublicArticle/17631 2021-09-03T06:10:17+09:00 2021-09-03T06:10:17+09:00 https://crieit.net/posts/java-Maven-CompilationFailureException-Compilation-failure (solved) Maven: CompilationFailureException: Compilation failure <p><code>JAVA_HOME</code> が設定されていなかったというありがちなやつですが、次に同じことが起こったときに手間取らないように覚書。</p> <h1 id="状況"><a href="#%E7%8A%B6%E6%B3%81">状況</a></h1> <p>対象: <a target="_blank" rel="nofollow noopener" href="https://pdfbox.apache.org/">Apache PDFBox</a>(<a target="_blank" rel="nofollow noopener" href="https://github.com/apache/pdfbox">GitHub mirror</a>) 2.0.21</p> <p>README にしたがって <code>mvn clean install</code> を実行するとコンパイルで失敗する。</p> <p>スタックトレース:</p> <pre><code>[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.6.0:compile (default-compile) on project fontbox: Compilation failure -> [Help 1] org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.6.0:compile (default-compile) on project fontbox: Compilation failure at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:215) at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:156) at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:148) at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:117) at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:81) at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build (SingleThreadedBuilder.java:56) at org.apache.maven.lifecycle.internal.LifecycleStarter.execute (LifecycleStarter.java:128) at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:305) at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:192) at org.apache.maven.DefaultMaven.execute (DefaultMaven.java:105) at org.apache.maven.cli.MavenCli.execute (MavenCli.java:972) at org.apache.maven.cli.MavenCli.doMain (MavenCli.java:293) at org.apache.maven.cli.MavenCli.main (MavenCli.java:196) at sun.reflect.NativeMethodAccessorImpl.invoke0 (Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke (Method.java:498) at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced (Launcher.java:282) at org.codehaus.plexus.classworlds.launcher.Launcher.launch (Launcher.java:225) at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode (Launcher.java:406) at org.codehaus.plexus.classworlds.launcher.Launcher.main (Launcher.java:347) Caused by: org.apache.maven.plugin.compiler.CompilationFailureException: Compilation failure at org.apache.maven.plugin.compiler.AbstractCompilerMojo.execute (AbstractCompilerMojo.java:1033) at org.apache.maven.plugin.compiler.CompilerMojo.execute (CompilerMojo.java:137) at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo (DefaultBuildPluginManager.java:137) at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:210) at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:156) at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:148) at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:117) at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:81) at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build (SingleThreadedBuilder.java:56) at org.apache.maven.lifecycle.internal.LifecycleStarter.execute (LifecycleStarter.java:128) at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:305) at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:192) at org.apache.maven.DefaultMaven.execute (DefaultMaven.java:105) at org.apache.maven.cli.MavenCli.execute (MavenCli.java:972) at org.apache.maven.cli.MavenCli.doMain (MavenCli.java:293) at org.apache.maven.cli.MavenCli.main (MavenCli.java:196) at sun.reflect.NativeMethodAccessorImpl.invoke0 (Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke (Method.java:498) at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced (Launcher.java:282) at org.codehaus.plexus.classworlds.launcher.Launcher.launch (Launcher.java:225) at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode (Launcher.java:406) at org.codehaus.plexus.classworlds.launcher.Launcher.main (Launcher.java:347) </code></pre> <h1 id="環境"><a href="#%E7%92%B0%E5%A2%83">環境</a></h1> <pre><code>Ubuntu 18.04 openjdk version "1.8.0_292" (openjdk-8-jdk:amd64) Apache Maven 3.8.2 maven-compiler-plugin 3.6.0 </code></pre> <h1 id="メモ"><a href="#%E3%83%A1%E3%83%A2">メモ</a></h1> <p><code>-X (--debug)</code> オプションを付けて実行すると詳しい情報が出力される。<br /> 今回ヒントになったのはこのあたり:</p> <pre><code>$ mvn -X clean install (snip) [INFO] --- maven-compiler-plugin:3.6.0:compile (default-compile) @ fontbox --- (snip) [DEBUG] Configuring mojo 'org.apache.maven.plugins:maven-compiler-plugin:3.6.0:compile' with basic configurator --> [DEBUG] (f) basedir = (snip)/pdfbox/fontbox [DEBUG] (f) buildDirectory = (snip)/pdfbox/fontbox/target [DEBUG] (f) compilePath = [(snip)/pdfbox/fontbox/target/classes, /home/(USER)/.m2/repository/commons-logging/commons-logging/1.2/commons-logging-1.2.jar] [DEBUG] (f) compileSourceRoots = [(snip)/pdfbox/fontbox/src/main/java] [DEBUG] (f) compilerId = javac [DEBUG] (f) debug = true [DEBUG] (f) encoding = UTF-8 [DEBUG] (f) executable = ${env.JAVA_HOME}/bin/javac [DEBUG] (f) failOnError = true [DEBUG] (f) failOnWarning = false [DEBUG] (f) forceJavacCompilerUse = false [DEBUG] (f) fork = true [DEBUG] (f) generatedSourcesDirectory = (snip)/pdfbox/fontbox/target/generated-sources/annotations [DEBUG] (f) mojoExecution = org.apache.maven.plugins:maven-compiler-plugin:3.6.0:compile {execution: default-compile} [DEBUG] (f) optimize = false [DEBUG] (f) outputDirectory = (snip)/pdfbox/fontbox/target/classes [DEBUG] (f) project = MavenProject: org.apache.pdfbox:fontbox:2.0.21 @ (snip)/pdfbox/fontbox/pom.xml [DEBUG] (f) projectArtifact = org.apache.pdfbox:fontbox:bundle:2.0.21 [DEBUG] (f) session = org.apache.maven.execution.MavenSession@31464a43 [DEBUG] (f) showDeprecation = true [DEBUG] (f) showWarnings = false [DEBUG] (f) skipMultiThreadWarning = false [DEBUG] (f) source = 1.6 [DEBUG] (f) staleMillis = 0 [DEBUG] (f) target = 1.6 [DEBUG] (f) useIncrementalCompilation = true [DEBUG] (f) verbose = false [DEBUG] -- end configuration -- [DEBUG] Using compiler 'javac'. (snip) [DEBUG] Excutable: [DEBUG] ${env.JAVA_HOME}/bin/javac [DEBUG] Command line options: [DEBUG] (snip) -target 1.6 -source 1.6 -encoding UTF-8 (snip) </code></pre> <p>javac のパスが</p> <pre><code>[DEBUG] (f) executable = ${env.JAVA_HOME}/bin/javac </code></pre> <p>となっていて、環境変数 <code>JAVA_HOME</code> が期待されているようだったので、</p> <pre><code>JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 mvn clean install </code></pre> <p>のように指定したらコンパイルできるようになった。</p> <p>バージョンが 1.6 となっているのも気にはなって、たぶん正しく指定した方がいいんだろうけどそっちは修正しなくてもよかった。理由は不明。</p> <hr /> <p>ついでにメモ。</p> <p><code>compile</code> ゴールのヘルプ表示コマンド:</p> <pre><code>mvn compiler:help -Ddetail=true -Dgoal=compile </code></pre> sonota486 tag:crieit.net,2005:PublicArticle/16668 2021-02-02T05:14:55+09:00 2021-02-02T05:15:39+09:00 https://crieit.net/posts/Java-LibreOffice-Calc-fods-2019 JavaでLibreOffice Calcのfodsファイルを読み書きするサンプル 2019 <p><a target="_blank" rel="nofollow noopener" href="https://memo88.hatenablog.com/entry/20140726/1406375184">5年前に JavaScript で書いたもの</a> を大体そのまま Java に書き直しただけです。例外のハンドリングは適当です。今では推奨されない古い書き方が残ってたりするかもしれません。</p> <p>sonota88/libreoffice-java-sample at 20191202<br /> <a target="_blank" rel="nofollow noopener" href="https://github.com/sonota88/libreoffice-java-sample/tree/20191202">https://github.com/sonota88/libreoffice-java-sample/tree/20191202</a></p> <p>処理の内容的には fods ファイルを開いてセルの内容の最低限の読み書きするというもの。</p> <p>(※ <a target="_blank" rel="nofollow noopener" href="https://memo88.hatenablog.com/entry/2019/12/02/070925">2019-12-02 に書いた記事</a>のクロス投稿です)<br /> (※ 2021-01-01 追記: 2021年版書きました → <a target="_blank" rel="nofollow noopener" href="https://memo88.hatenablog.com/entry/2021/01/01/155345">JavaでLibreOffice Calcのfodsファイルを読み書きするサンプル 2021</a>)</p> <hr /> <p>以下は今回調べたりしたことのメモです。開発環境は Ubuntu 18.04。</p> <h1 id="jar の設定を pom.xml に書く"><a href="#jar+%E3%81%AE%E8%A8%AD%E5%AE%9A%E3%82%92+pom.xml+%E3%81%AB%E6%9B%B8%E3%81%8F">jar の設定を pom.xml に書く</a></h1> <p>Java から LibreOffice の API を使う場合、 SDK をインストールして、それに付いてくる jar を使う、というのが普通のやり方だったと思います。たしか。</p> <p>Eclipse の場合は</p> <ul> <li>プロジェクトのプロパティ</li> <li>Java Build Path>「Libraries」タブ>Add External JARs...</li> </ul> <p>から追加します。ここで jar を追加すると、プロジェクトの <code>.classpath</code> ファイルに</p> <pre><code class="xml"><classpathentry kind="lib" path="/usr/lib/libreoffice/program/classes/juh.jar"/> </code></pre> <p>このような設定が追加されます。</p> <p>これだと Eclipse 用の設定になってしまうので、<code>pom.xml</code> に書けないんだっけと思って調べたところ、下記のように system スコープで dependency を書けばよいようでした。<br /> (groupId, artifactId、バージョンは適当です)</p> <pre><code class="xml"><dependency> <groupId>juh-g</groupId> <artifactId>juh-a</artifactId> <version>0.0.1</version> <scope>system</scope> <systemPath>/usr/lib/libreoffice/program/classes/juh.jar</systemPath> </dependency> </code></pre> <p>参考:</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Scope">Maven – Introduction to the Dependency Mechanism: Dependency Scope</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.techscore.com/tech/Java/ApacheJakarta/Maven/3/#maven-3-1-1">3. Maven 入門 (2) | TECHSCORE(テックスコア): 3.1.1. 依存性の指定とスコープ</a></li> </ul> <p>ただ、 jar はこれでいけるんですが、共有ライブラリ <code>libjpipe.so</code> は <code>pom.xml</code> で設定できるか分からず、これだけ Eclipse 側で設定しました。<br /> (Eclipse 上でユニットテストなどで実行するときに必要で、プログラム書いてコンパイルするだけなら不要っぽいです)</p> <p>関連: <a target="_blank" rel="nofollow noopener" href="https://memo88.hatenablog.com/entry/20140723/1406123992">(solved) Exception in thread "main" java.lang.UnsatisfiedLinkError: no jpipe in java.library.path</a></p> <h1 id="Ubuntu 18.04 でのパッケージまわりのメモ"><a href="#Ubuntu+18.04+%E3%81%A7%E3%81%AE%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E3%81%BE%E3%82%8F%E3%82%8A%E3%81%AE%E3%83%A1%E3%83%A2">Ubuntu 18.04 でのパッケージまわりのメモ</a></h1> <pre><code>libreoffice-java-common ... unoil を含む libreoffice-common ... ure に依存 ure ... juh, jurt, ridl, jpipe を含む dpkg や apt-cache コマンドで調べられます: パッケージに含まれるファイル一覧 dpkg -L {パッケージ名} パッケージの依存関係 apt-cache depends {パッケージ名} apt-cache rdepends {パッケージ名} </code></pre> <p>ところで <code>libreoffice-dev</code> というパッケージもありますがこれって何でしたっけ?</p> <pre><code>$ apt-cache depends libreoffice-dev libreoffice-dev Depends: libreoffice-core Depends: libreoffice-dev-common Depends: ucpp Depends: libc6 Depends: libgcc1 Depends: libstdc++6 Depends: libx11-6 Depends: uno-libs3 Depends: ure Conflicts: libreoffice Conflicts: libreoffice-dev-doc Breaks: libreoffice-dev-common Recommends: g++ Recommends: libreoffice-java-common |Recommends: default-jre |Recommends: <sun-java6-jre> |Recommends: <java6-runtime> default-jre openjdk-11-jre openjdk-8-jre Recommends: <jre> Suggests: libmythes-dev Suggests: libreoffice-dev-doc Suggests: libreofficekit-dev Replaces: libreoffice-dev-common </code></pre> <p>なるほど。パッケージの説明は</p> <blockquote> <p>office productivity suite -- SDK -- architecture-dependent parts</p> </blockquote> <p>となっています。</p> <h1 id="Maven のリポジトリにある jar を使う"><a href="#Maven+%E3%81%AE%E3%83%AA%E3%83%9D%E3%82%B8%E3%83%88%E3%83%AA%E3%81%AB%E3%81%82%E3%82%8B+jar+%E3%82%92%E4%BD%BF%E3%81%86">Maven のリポジトリにある jar を使う</a></h1> <p>共有ライブラリ libjpipe.so を除くと、他は jar を使っているだけといえばだけです(たぶん)。それなら、ひょっとして Maven のリポジトリから取ってきて普通の Maven プロジェクトっぽくできたりしないでしょうか?</p> <p>探したら Maven のセントラルリポジトリにありました。</p> <p>"org.libreoffice" の検索結果:<br /> <a target="_blank" rel="nofollow noopener" href="https://search.maven.org/search?q=org.libreoffice">https://search.maven.org/search?q=org.libreoffice</a></p> <p>これを使えば、 <code>libreoffice-java-common</code> をインストールしなくても必要な jar を Maven の流儀に従って使えばよく、より普通の Maven プロジェクトっぽく扱えて嬉しいような。</p> <p><code>pom.xml</code> に普通にこんな感じで書けばよいと。普通ですね。いいですね。</p> <pre><code class="xml"> <dependency> <groupId>org.libreoffice</groupId> <artifactId>ridl</artifactId> <version>6.3.2</version> </dependency> </code></pre> <p>ふむふむ、いいじゃない、となったのですが、この方法だと 5年前のこれと同じところで引っかかるのです……。</p> <p>(solved) com.sun.star.comp.helper.BootstrapException: no office executable found!<br /> <a target="_blank" rel="nofollow noopener" href="https://memo88.hatenablog.com/entry/20140721/1405966864">https://memo88.hatenablog.com/entry/20140721/1405966864</a></p> <p>上記の記事から5年経ちましたが、 <code>Bootstrap</code> クラスが含まれている juh.jar の位置を起点にして実行ファイル soffice を探す部分は変わっていないようで、今回のサンプルでは Maven のリポジトリを利用する方向は見送りました。</p> <p>Maven でライブラリ取ってくると <code>~/.m2/</code> 以下に jar が入ったり、fat jar 作ったらその中に入ったりするので、そこから相対パスで探しても soffice が見つけられないんですよね……。</p> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/LibreOffice/core/blob/libreoffice-6-4/javaunohelper/com/sun/star/comp/helper/Bootstrap.java">本体のコード(Bootstrap.java)</a> を借用&修正して使っても動きましたが、その場合は公開の際に本体のライセンスに従う必要があるでしょう。</p> <h1 id="Docker コンテナで実行する"><a href="#Docker+%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%81%A7%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B">Docker コンテナで実行する</a></h1> <p>先日 <a target="_blank" rel="nofollow noopener" href="https://memo88.hatenablog.com/entry/2019/11/03/090019">LibreOffice 本体だけ Docker で動かすメモ</a><br /> を書きましたが、ついでに SDK もイメージに入れておけば便利かも? と思いついて、これも試してみました。</p> <p>以下の3つのパッケージを入れておけば今回のサンプルは動きました。</p> <ul> <li><code>libreoffice-calc</code></li> <li><code>libreoffice-java-common</code></li> <li><code>openjdk-8-jre</code></li> </ul> <p>あとはコンテナ内で</p> <pre><code>java -cp "{ライブラリのパス}:{ビルドしたjarのパス}" \ sample.Main {残りの引数} </code></pre> <p>で実行できます。 詳しくは<a target="_blank" rel="nofollow noopener" href="https://github.com/sonota88/libreoffice-java-sample/tree/20191202">リポジトリ</a>に入っているスクリプト <code>run.sh</code> と <code>Dockerfile</code> を見てください。</p> sonota486 tag:crieit.net,2005:PublicArticle/16095 2020-10-04T23:14:35+09:00 2021-11-29T15:49:26+09:00 https://crieit.net/posts/UML 実践UML記述法:UMLのクラス図における関係の考察 <p>本記事は、 Qrunch からの移転記事です。</p> <p>移転元: <a target="_blank" rel="nofollow noopener" href="https://morichan.qrunch.io/entries/3n3Rsvp6njvRJP7l">UMLのクラス図における関係の考察 - 雑木林</a></p> <h1 id="はじめに"><a href="#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB">はじめに</a></h1> <p>UMLのクラス図には、ノードのようなもの(クラスやインタフェースなど)とエッジのようなもの(関連や依存など)を記述できます。</p> <p>さて、クラスやインタフェースをプログラミング言語に落とし込むには、あまり苦も無く変換できると思います。<br /> 私もクラス図とPerlの変換について、記事を書いたりしていました。</p> <blockquote> <p>クラス図とPerlの対応付け: <a target="_blank" rel="nofollow noopener" href="https://qiita.com/Morichan/items/cfa83fee6d451847ef85">https://qiita.com/Morichan/items/cfa83fee6d451847ef85</a></p> </blockquote> <p>しかし、エッジのようなもの(以降、関係と呼びます)は、汎化関係以外を書くのは難しいのが現状です。<br /> C++への変換において、「<a target="_blank" rel="nofollow noopener" href="https://www.researchgate.net/publication/220610417_Recovering_UML_class_models_from_C_A_detailed_explanation">Recovering UML class models from C++: A detailed explanation</a>」では、関係をC++に対応付けするためには意味解析 (semancit analysis) をする必要がある、と書かれています。</p> <p>ここでいう意味解析という意味ですが、恐らくは構文解析だけでは瞬時に変換できず、構文解析結果を元にメモ化テーブルなどを作り、作成したメモ化テーブルから総合的に判断する必要がある、という意味だと私は捉えました。<br /> 例えば、1つのクラス内に同じ名前のメンバ変数(Javaであればフィールドと呼びます)が複数存在する場合、コンパイルエラーとなりますよね。<br /> 本来であれば避けるべきですが、構文解析だけでは判断できません。<br /> 判断するためには、構文解析によってフィールドのリストをメモ化テーブルに入れておき、同じメンバ変数をメモ化テーブルから確認しなければなりません。<br /> 他にも連想配列を使うとか、いろいろ方法はあると思いますが、どちらにせよ構文解析「だけ」では無理だということです。<br /> これを「意味解析をする必要がある」という意味で使っていると考えます。</p> <p>さて、上記の論文において「意味解析をする必要がある」という意味が、私が捉えた意味だとするならば、構文解析を元に関係を変換できるのではないか、と考えることができます。<br /> 以降は、私が考えたサイキョーの変換ですので、深く考えずに読んでいただければと思います。</p> <h1 id="関係の種類と対応"><a href="#%E9%96%A2%E4%BF%82%E3%81%AE%E7%A8%AE%E9%A1%9E%E3%81%A8%E5%AF%BE%E5%BF%9C">関係の種類と対応</a></h1> <p>関係には、以下の6つがあります。</p> <ul> <li>実現</li> <li>汎化</li> <li>コンポジション</li> <li>集約</li> <li>関連</li> <li>依存</li> </ul> <p>上部の関係が最も強く、下がるにつれて関係が弱くなります。</p> <p>余談ですが、「関係」と言えば上記6つの関係を総称したものとして取り、「関連」と言えば関係の中の1種類のものとして取ります。<br /> よく勘違いしやすいので気を付けましょう。</p> <p>以降、各関係をどう捉えるか、Javaを例にして考えてみます。</p> <h2 id="実現"><a href="#%E5%AE%9F%E7%8F%BE">実現</a></h2> <p>いわゆるインタフェースを実装しているクラスの関係です。<br /> クラス図では、下図のように記述します。</p> <p><img src="http://www.plantuml.com/plantuml/png/SoWkIImgAStDuShCAqajIajCJbLutBZrSLC_Z9p-k77-uwQcnuth5AgvQhcuadCIYuiLdgwR_s9nAufiPcv1JcfkQbv9CNuJ75BpKe0M0W00" alt="実現関係のクラス図" /></p> <p>Javaにおいてはinterfaceとimplementの関係を書けばよいです。</p> <pre><code class="java">interface ふわふわしたもの { // ... } class 実装したもの implements ふわふわしたもの { // ... } </code></pre> <p>簡単ですね。</p> <h2 id="汎化"><a href="#%E6%B1%8E%E5%8C%96">汎化</a></h2> <p>いわゆる継承クラスの関係です。<br /> クラス図では、下図のように記述します。</p> <p><img src="http://www.plantuml.com/plantuml/png/SoWkIImgAStDuKfCAYufIamkKNZMjVV5neNFPxKyRbprkAdfSUCwBamX1PiQNLs5fwtRqPL2X32LFcxgUDpKXKM3GsfU2j1-0000" alt="汎化関係のクラス図" /></p> <p>クラス図では、抽象クラスとクラスの汎化関係、およびクラスとクラスの汎化関係の間に差異はありません。</p> <p>Javaにおいてはextendsの関係を書けばよいです。</p> <pre><code class="java">abstract class 抽象的なもの { // ... } class 具象的なもの extends 抽象的なもの { // ... } class より具象的なもの extends 具象的なもの { // ... } </code></pre> <p>こちらも簡単ですね。</p> <p>つまり、実現と汎化については、構文解析レベルで判断可能です。<br /> ですから、これ以降について考えてみると面白いですよ。<br /> 以降は答えがありませんので、各自考えてみてください。</p> <h2 id="コンポジション"><a href="#%E3%82%B3%E3%83%B3%E3%83%9D%E3%82%B8%E3%82%B7%E3%83%A7%E3%83%B3">コンポジション</a></h2> <p>is-part-of関係と呼ばれる関係です。<br /> クラス図では、下図のように記述します。</p> <p><img src="http://www.plantuml.com/plantuml/png/SoWkIImgAStDuKhEIImkLb1wsRpYQKVRyrajZnjNFMvgUjouJaaXiLZ1EICp9uKBfkXfwuedUoTZA0f8B4hDA-7Y0YjMKD9LK7ZP35JOwwYaOAEhgmMJTqZDIm7Q2000" alt="コンポジション関係のクラス図" /></p> <p>ここで、関連端名として「部位A」という文字と、その左端に可視性の「-」 (private) が登場しました。<br /> Javaで記述する際に利用するので、併せて覚えてください。</p> <p>よく勘違いされる方がいますが、黒塗り菱形は持っている側に付きます。<br /> 持たれる側に付くわけではありません、少しややこしいですよね。<br /> また、持たれる側の矢印はオプションです、あってもなくてもあまり意味はありません。</p> <p>さて、Javaでどう書くかというと、私は「そのクラスだけがインスタンスを持っている」と捉えます。</p> <pre><code class="java">class 部分的なもの { // ... } class 全体的なもの { private 部分的なもの 部位A = new 部分的なもの(); } </code></pre> <p>new演算子以降については、あっても無くても問題ありません。<br /> あるいは、コンストラクタ内に記述するというのも手でしょう。</p> <pre><code class="java">class 部分的なもの { // ... } class 全体的なもの { private 部分的なもの 部位A; 全体的なもの() { 部位A = new 部分的なもの(); } } </code></pre> <p>さて、これだけだと「ふーん、そんなものか」だと思われると思いますが、次の集約を見れば違いがはっきりすると思います。</p> <h2 id="集約"><a href="#%E9%9B%86%E7%B4%84">集約</a></h2> <p>own-a関係と呼ばれる関係です。<br /> クラス図では、下図のように記述します。</p> <p><img src="http://www.plantuml.com/plantuml/png/SoWkIImgAStDuKhEIImkLb1wrjFuk77-uwQUnutB7ZUsF6xgVjpKzBXndP92Oh62SqPcJWeNYjB5sDG591QbvfLmSO7LAoZfAZphR02gVZgvqL59mKPNDn-cmY4rBmLeEG00" alt="集約関係のクラス図" /></p> <p>さて、コンポジションとの違いは、黒塗り菱形を白抜き菱形に変更しただけです。<br /> この違いですが、is-part-of関係(つまり「全体的なもの」は「部分的なもの」の一部である関係)とown-a関係(つまり「持っているもの」は「持たれているもの」を所有している)という言葉の違いである、と考えました。</p> <p>すなわち、Javaでどう書くかというと、私は「そのクラス以外にもインスタンスを(参照として)保持している」と捉えます。</p> <pre><code class="java">class 持たれているもの { // ... } class 持っているもの { private 持たれているもの 所持品X; 持っているもの() { 所持品X = new 持たれているもの(); } } </code></pre> <p>違い無しじゃないか!<br /> と思われたあなた、正解です。<br /> 上記のクラス図では違いはありません。<br /> しかし、下図でははっきり違いが見えると思います。</p> <p><img src="http://www.plantuml.com/plantuml/png/SoWkIImgAStDuNhMq_YuSVxZffx7ZSiUDxOyRkf-tDJqk76TWfAkmhFE1g3qqycD4KeADhgw-GfWnOlHLE8n8YOFoP0eg27FroryFg7jSUDqDEN4EIKIQWfOgitmSUCIe0eyxbgCQHZK1K_NxtknSI74QrguNOLk4M4Z8maPwP2Qbm8CUm00" alt="複数の集約関係のクラス図" /></p> <pre><code class="java">class 持たれているもの { // ... } class 持っているもの { private 持たれているもの 所持品X; 持っているもの(持たれているもの 所持品) { 所持品X = 所持品; } } class もう1つの持っているもの { private 持たれているもの 所持品Y; もう1つの持っているもの(持たれているもの 所持品) { 所持品X = 所持品; } } class 管理しているもの { private 持っているもの 管理対象X; private もう1つの持っているもの 管理対象Y; private 持たれているもの 所持品Z = new 持たれているもの(); 管理しているもの() { 管理対象X = new 持っているもの(所持品Z); 管理対象Y = new もう1つの持っているもの(所持品Z); } } </code></pre> <p>「管理しているもの」クラスのコンストラクタによって、「持っているもの」クラスのコンストラクタと「もう1つの持っているもの」クラスのコンストラクタに、所持品Zインスタンスを代入しています。<br /> Javaではインスタンスは基本的に参照渡しのため、「持っているもの」クラスの所持品Xと「もう1つの持っているもの」クラスの所持品Yと「管理しているもの」クラスの所持品Zは全て同じインスタンスを参照していることになります。</p> <p>これが、私の考える集約です。</p> <h2 id="関連"><a href="#%E9%96%A2%E9%80%A3">関連</a></h2> <p>has-a関係と呼ばれる関係です。<br /> クラス図では、下図のように記述します。</p> <p><img src="http://www.plantuml.com/plantuml/png/SoWkIImgAStDuNenOU_ZneMF6vSzRcnvtDJzkAdfSUCw1ILTXUUTAv_idHoWf62Zgwlmj7yQN9BB8JKl1QWL0000" alt="関連関係のクラス図" /></p> <p>実は、Javaにおいて一番ややこしい関係は、この関連だと考えます。<br /> すなわち、Javaでどう書くかというと、私は「実現、汎化、コンポジション、集約、依存のいずれにもあたらないが、依存より強い関係を持つ」と捉えます。</p> <pre><code class="java">class 落ちているもの { // ... } class 取るもの { void hoge() { 落ちているもの 物体P = new 落ちているもの(); } } </code></pre> <p>物体Pはhoge()メソッド内でのみ生存できます。<br /> つまり、「取るもの」クラスの属性にはあたりません。</p> <p>基本的に、関連端名を書ける関係は、インライン属性として同じように記述できます。<br /> コンポジションのクラス図における「全体的なもの」クラスと同義のインライン属性を持つクラスの図を、下図に示します。</p> <p><img src="http://www.plantuml.com/plantuml/png/SoWkIImgAStDuIhEpimhI2nAp5L8paaiBdOiAIdAJ2ejIVLCpiyBpgnALJ3WKeT8AIYzR5xnjAFj-QoMnush7ZSrFMvSfwIGMApWd96PayB51LNqj7N5axsJCHG591QbvfKeL7Cf084k4Bzi1ghhTLImKi0qbgkMoo4rBmNeFW00" alt="コンポジションのクラス図における「全体的なもの」クラスと同義のインライン属性を持つクラス" /></p> <p>これは逆に言えば、「全体的なもの」クラスの内部において、部位A属性をどの操作からでも参照できることを意味します。</p> <p>しかし、関連における「取るもの」クラスの持つ物体P属性は、特定の操作内(メソッド内)でのみ生存するため、「取るもの」クラス内の別の操作が利用することはできません。<br /> そのため、関連における関連端名をインライン属性には変換できません。</p> <p>UMLの仕様ではどうだったか定かではありませんが、確か全ての関係はインライン属性に変換可能だったような気がします [要検証] 。<br /> そのため、もしかしたらUMLの仕様とバッティングする恐れがあるので、この辺りは詳しく調査する必要がありそうです。</p> <h2 id="依存"><a href="#%E4%BE%9D%E5%AD%98">依存</a></h2> <p>とても範囲の広い関係です。<br /> クラス図では、下図のように記述します。</p> <p><img src="http://www.plantuml.com/plantuml/png/SoWkIImgAStDuIhEpimhI2nAp5L8paaiBdOiAIdAJ2ejIVLCpiyBpgnALJ3W0aieA3tPD_VfsXcF6wS-RkfvtBZiSMFBuwRknqrDZnlNAIaa5YiuPsJcf72XgvwEJMb0Ob5gNeb2DLSj5nTOcuADdXvKbCiXDIy5w380" alt="依存関係のクラス図" /></p> <p>依存と聞くと、上記までの5つの関係全ても依存に書換えられる気がします。<br /> しかし、UML2.5.1の仕様では、依存 (Dependency) は直接関係 (DirectedRelationship) と汎化関係であり、関連 (Association) と汎化 (Extension) とは異なるものとして定義されています。<br /> 実現 (InterfaceRealization) は依存と汎化関係であるため、実現については依存に書換えようと思えばできる関係と言えるでしょう。<br /> ちなみに、集約 (Aggregation) とコンポジション (Composition) は、属性 (Property) の種類を表す列挙子 (AggregationKind) として設定できるみたいで、こちらも全く異なります。</p> <p><img src="http://www.plantuml.com/plantuml/png/SoWkIImgAStDuKfCAYufIamkKGXApKaioSpFAyx8B428kcGMfIQNfAQWo78XynHiQdHriQl3LEPON9oVd0aMv89hmJGijI0rBoKrBxcI2y646W1n6DW093P4qZEl9BKeBJ4vDIWrCIUp2c8Dr4g4AjUAajIyOf2K0z1eO1W00000" alt="UML2.5.1の仕様における関係の定義" /></p> <p>UML2.5.1の仕様では確かに書換えられないようですが、実際問題として「依存」という名前だけではよくわかりません。<br /> ではUML2.5.1の仕様を読めば解決するのかというと、そうでもなさそうです。</p> <blockquote> <p>A Dependency is a Relationship that signifies that a single model Element or a set of model Elements requires other model Elements for their specification or implementation.</p> <p>意訳: 依存は、とあるクラス(ここでは要素ではなくクラスと読んで差し支えない)の仕様や実装のために、別のクラスを必要としていることを意味する。</p> </blockquote> <p>つまり、実装に必要であればいいようです。</p> <p>ということで、Javaでどう書くかというと、私は「依存先のクラスをどこかしらで利用しており、上記5つの関係以外の場合全てを表す」と捉えます。</p> <pre><code class="java">class 依存されているもの { // ... } class 依存しているもの { // ... void hoge(依存されているもの 引数S) { // ... } } </code></pre> <p>例えば上記のように、メソッドの引数の型について、現状クラス図では定義することが難しいです。<br /> ですが、依存を書くことで、どこか関係性があるのだろうことは理解できます。</p> <p>他にも、キャストのためだけに呼ばれているとか、調べればいろいろ利用法が出てきそうです。<br /> どちらにせよ、クラス図からJavaに変換する際には利用できそうにありません。<br /> Javaからクラス図に変換する際に利用する関係だと思います。</p> <p>煩雑になりすぎてしまう問題は、容易に想像できます。<br /> しかし、UMLはいわゆるダイアグラムですから、読みづらければ非表示にすればよいだけです。<br /> 全てを表示する必要はなく、ダイアグラムの利用目的のために表示内容を変えるのは手だと思います。</p> <h1 id="おわりに"><a href="#%E3%81%8A%E3%82%8F%E3%82%8A%E3%81%AB">おわりに</a></h1> <p>私がUMLを研究していて、いつか挑戦してみたいことを書いてみました。<br /> もう修士課程は終盤直前ですが、もしこの話に興味があれば、勝手に使ってかまいません。<br /> もちろん、このサイトから引用したなんて野暮なことは書かず、自分で定義し、その理由付けも自分で行ってくださいね。<br /> その論文ができたら、ご一報くださると嬉しいです。</p> <p>UMLに未来があることを願います。</p> Morichan tag:crieit.net,2005:PublicArticle/16077 2020-09-27T20:05:06+09:00 2020-09-27T20:05:06+09:00 https://crieit.net/posts/5a40eebd9e5716d5cca625f0bd58c9b7 愚直に書いて見事に爆死...... <p>AtCoder Grand Contest 039 C - Division by Two with Something https://atcoder.jp/contests/agc039/tasks/agc039_c<br /> 私が見かけに騙されてしまい、簡単そうだと思って正攻法で書いたら大変残念なことになった。 私は残念ながら数弱なのでビット反転は思いつかなかった。。。<br /> 一応githubに置いてみた。 https://github.com/oooonduke/Java</p> oooonduke tag:crieit.net,2005:PublicArticle/15753 2020-03-09T10:04:51+09:00 2020-03-09T10:04:51+09:00 https://crieit.net/posts/Java-JSON-lib-JSON-Date 【Java】JSON-libを使ってJSON配列を生成する際に日付型(Date)を好きなフォーマットで変換する方法 <p>最近はWEBのサービス開発などでSPA(Single Page Applicatoin)が流行って?るので、<br /> サーバーとクライアントとのデータのやり取りをJSONで行うのはよくあるケースかと思います。</p> <p>サーバー側をJavaで開発している場合に、Javaオブジェクトや Bean を JSONに変換するのに、<br /> JSON-lib というのも結構主流なやり方かと思いますが、ちょっと日付型のところでハマったので備忘的な記事を。</p> <p><span style="color: #de2a2a;">※JSON-lib 自体の詳しい説明(利用方法)などは省略します。</span></p> <h2 id="JSON-libとは"><a href="#JSON-lib%E3%81%A8%E3%81%AF">JSON-libとは</a></h2> <blockquote><a target="_blank" rel="nofollow noopener" href="https://ja.osdn.net/projects/sfnet_json-lib/" target="_blank" rel="noopener noreferrer">OSDNサイト</a>より転載 Json-libは、beans, map, collection, Java配列、XML to JSON, DynaBeansを変革するJavaライブラリです。 Douglas Crockfordによる作業をベースにしています。</blockquote> <p>Javaオブジェクトを簡単に JSON文字列に変換したり、逆に JSON文字列からオブジェクトを生成したりといったことが可能です。</p> <p>昔、XMLへの相互変換ロジックを自作したことがありますが、こういう処理って実際に作ってみると結構大変だったりします。<br /> 便利な世の中ですね。</p> <h2 id="サンプル"><a href="#%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB">サンプル</a></h2> <p>変換するサンプルクラスを作成。</p> <p>Person.java</p> <pre><code>package jp.co.doraxdora.sample; import java.io.Serializable; import java.util.Date; /** * パーソンクラス * * @author doraxdora * */ public class Person implements Serializable { /** ID */ private int id; /** 氏名 */ private String name; /** 年齢 */ private int age; /** 更新日時 */ private Date updateDate; public Person() { } /** * パーソンの生成 * * @param id * @param name * @param age */ public Person(int id, String name, int age, Date updateDate) { this.id = id; this.name = name; this.age = age; this.updateDate = updateDate; } /** * @return id */ public int getId() { return id; } /** * @param id セットする id */ public void setId(int id) { this.id = id; } /** * @return name */ public String getName() { return name; } /** * @param name セットする name */ public void setName(String name) { this.name = name; } /** * @return age */ public int getAge() { return age; } /** * @param age セットする age */ public void setAge(int age) { this.age = age; } /** * @return updateDate */ public Date getUpdateDate() { return updateDate; } /** * @param updateDate セットする updateDate */ public void setUpdateDate(Date updateDate) { this.updateDate = updateDate; } } </code></pre> <p>何故か更新日時を持つパーソンクラスです。</p> <h3 id="JavaオブジェクトからJSON文字列に変換"><a href="#Java%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%8B%E3%82%89JSON%E6%96%87%E5%AD%97%E5%88%97%E3%81%AB%E5%A4%89%E6%8F%9B">JavaオブジェクトからJSON文字列に変換</a></h3> <pre><code>import net.sf.json.JSONObject; public class HelloWorld { public static void main(String[] args) { Person person = new Person(1, "サンプル 一郎", 20, new Date()); JSONObject jsonObject = JSONObject.fromObject(person); System.out.println(String.valueOf(jsonObject)); } } </code></pre> <p>こんな感じで、Dateを持つクラスをJSONに変換すると次のような文字列が生成されます。</p> <pre><code>{ "age": 20, "id": 1, "name": "サンプル 一郎", "updateDate": { "date": 23, "day": 2, "hours": 10, "minutes": 22, "month": 3, "seconds": 44, "time": 1555982564167, "timezoneOffset": -540, "year": 119 } } </code></pre> <p>これはこれで正しいのですが、クライアント側で日時の部分が扱いづらい。。</p> <h2 id="対応"><a href="#%E5%AF%BE%E5%BF%9C">対応</a></h2> <p>JSON-lib には、「JsonValueProcessor」という、変換時に対象となる値に対して処理できるインターフェースが定義されているので、<br /> 実装したクラスを作成して日付型オブジェクトに対して別途変換する処理を組み込みます。</p> <p>DateJsonValueProcessor.java</p> <pre><code>package jp.co.doraxdora.sample; import java.util.Date; import net.sf.json.JsonConfig; import net.sf.json.processors.JsonValueProcessor; import java.text.SimpleDateFormat; /** * 日付型をJSON変換する際にフォーマットする * * @author doraxdora * */ public class DateJsonValueProcessor implements JsonValueProcessor { /* (非 Javadoc) * @see net.sf.json.processors.JsonValueProcessor#processArrayValue(java.lang.Object, net.sf.json.JsonConfig) */ @Override public Object processArrayValue(Object value, JsonConfig jsonConfig) { return process((Date) value, jsonConfig); } /* (非 Javadoc) * @see net.sf.json.processors.JsonValueProcessor#processObjectValue(java.lang.String, java.lang.Object, net.sf.json.JsonConfig) */ @Override public Object processObjectValue(String key, Object value, JsonConfig jsonConfig) { return process((Date) value, jsonConfig); } /** * 日付型を文字列に変換. * * @param date * @param config * @return */ private Object process(Date date, JsonConfig config) { if (date == null) { return null; } SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd(E)"); return formatter.format(date); } } </code></pre> <h3 id="JsonConfig に DateValueProcessor を設定して変換"><a href="#JsonConfig+%E3%81%AB+DateValueProcessor+%E3%82%92%E8%A8%AD%E5%AE%9A%E3%81%97%E3%81%A6%E5%A4%89%E6%8F%9B">JsonConfig に DateValueProcessor を設定して変換</a></h3> <pre><code>import net.sf.json.JSONObject; import net.sf.json.JsonConfig; public class HelloWorld { public static void main(String[] args) { Person person = new Person(1, "サンプル 一郎", 20, new Date()); JsonConfig config = new JsonConfig(); config.registerJsonValueProcessor(java.util.Date.class, new DateJsonValueProcessor()); JSONObject jsonObject = JSONObject.fromObject(person, config); System.out.println(String.valueOf(jsonObject)); } } </code></pre> <p>という感じで、日付型だった場合の処理を設定して変換してあげると次のような文字列が生成されます。</p> <pre><code>{ "age": 20, "id": 1, "name": "サンプル 一郎", "updateDate": "2019/04/23(火)" } </code></pre> <p>大分シンプルになりました。</p> <h2 id="まとめ"><a href="#%E3%81%BE%E3%81%A8%E3%82%81">まとめ</a></h2> <p>ちょっとした単発のプログラムネタでした。</p> <p>実は以前 Python でも同様の問題にぶち当たったことがあって、<br /> だったら Java でも同じようになんとかできるだろうと調べてみたという感じです。</p> <p>何かのお役に立てれば。</p> <p>ではでは。</p> <p> </p> doraxdora tag:crieit.net,2005:PublicArticle/15698 2020-01-27T10:01:36+09:00 2020-01-27T10:01:36+09:00 https://crieit.net/posts/JavaFX-Oracle-TableView 【JavaFX】Oracle に接続して取得したデータをTableView に表示する <p>引き続き、JavaFXで Oracle から取得したデータを TableView に表示したいと思います。</p> <p>プログラムは前回のものを流用します。</p> <p><a target="_blank" rel="nofollow noopener" href="https://www.doraxdora.com/blog/2017/09/30/post-2626/" target="_blank" rel="noopener">【JavaFX】Oracleに接続して取得したデータをコンボボックスに設定する</a></p> <h2 id="クラスの作成"><a href="#%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%AE%E4%BD%9C%E6%88%90">クラスの作成</a></h2> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxTblView000.jpg" alt="クラスの作成" /></p> <p>パッケージ・エクスプローラーから「dto」パッケージを右クリックし「新規」>「クラス」を選択します。</p> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxTblView001.jpg" alt="名前を入力" /></p> <p>クラス名に「TblCat」を入力し、「完了」ボタンをクリックします。</p> <p>作成したクラスの実装は次のようにします。</p> <p>TblCat.java</p> <pre><code>/** * */ package jp.co.doraxdora.dto; import java.io.Serializable; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; /** * TBL猫DTOクラス. * * @author doraxdora * */ public class TblCat implements Serializable { /** No */ private StringProperty no; /** 名前 */ private StringProperty name; /** 性別 */ private StringProperty sex; /** 年齢 */ private StringProperty age; /** 種別 */ private StringProperty kindCd; /** 好物 */ private StringProperty favorite; /** * @return no */ public StringProperty noProperty() { return no; } /** * @param no * セットする no */ public void setNo(String no) { this.no = new SimpleStringProperty(no); } /** * @return name */ public StringProperty nameProperty() { return name; } /** * @param name * セットする name */ public void setName(String name) { this.name = new SimpleStringProperty(name); } /** * @return sex */ public StringProperty sexProperty() { return sex; } /** * @param sex * セットする sex */ public void setSex(String sex) { this.sex = new SimpleStringProperty(sex); } /** * @return age */ public StringProperty ageProperty() { return age; } /** * @param age * セットする age */ public void setAge(String age) { this.age = new SimpleStringProperty(age); } /** * @return kind */ public StringProperty kindCdProperty() { return kindCd; } /** * @param kind * セットする kind */ public void setKindCd(String kindCd) { this.kindCd = new SimpleStringProperty(kindCd); } /** * @return favorite */ public StringProperty favoriteProperty() { return favorite; } /** * @param favorite * セットする favorite */ public void setFavorite(String favorite) { this.favorite = new SimpleStringProperty(favorite); } } </code></pre> <p> </p> <p><span style="color: #000000;">TblViewの仕組み上</span><br /> <span style="color: #000000;">通常のアクセサとは異なり、getter のメソッド名は [メンバ名]Property とします。<br /> こうしておかないと一覧にデータが表示されずにあたふたしますよ。</span></p> <h2 id="プログラム修正"><a href="#%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0%E4%BF%AE%E6%AD%A3">プログラム修正</a></h2> <h3 id="画面の修正"><a href="#%E7%94%BB%E9%9D%A2%E3%81%AE%E4%BF%AE%E6%AD%A3">画面の修正</a></h3> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxTblView002.jpg" alt="" /> メソッドの追加</p> <p>SceneBuilder からであれば、右側のメニューから「On Action」にメソッド名を設定しますが、<br /> fxmlを直接修正しても問題ありません。</p> <p>Smaple.fxml</p> <pre><code><?xml version="1.0" encoding="UTF-8"?> <import javafx.scene.control.Button> <import javafx.scene.control.ComboBox> <import javafx.scene.control.Label> <import javafx.scene.control.TableColumn> <import javafx.scene.control.TableView> <import javafx.scene.control.TextField> <import javafx.scene.layout.AnchorPane> <import javafx.scene.control.cell.PropertyValueFactory> <AnchorPane fx:id="mainPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="350.0" prefWidth="530.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.SampleController"> <children> <Label fx:id="lbName" layoutX="14.0" layoutY="33.0" text="名前:" /> <TextField fx:id="txName" layoutX="58.0" layoutY="29.0" /> <Label fx:id="lbKind" layoutX="221.0" layoutY="33.0" text="種別:" /> <ComboBox fx:id="cbKind" layoutX="264.0" layoutY="29.0" prefWidth="150.0" /> <Button fx:id="btSearch" layoutX="456.0" layoutY="29.0" mnemonicParsing="false" onAction="#btSearchButtonActionHandle" text="検索" /> <TableView fx:id="tvCat" layoutX="14.0" layoutY="63.0" prefHeight="273.0" prefWidth="500.0"> <columns> <TableColumn maxWidth="50.0" minWidth="50.0" prefWidth="50.0" resizable="false" text="No"> <cellValueFactory><PropertyValueFactory property="no"/></cellValueFactory> </TableColumn> <TableColumn maxWidth="100.0" minWidth="100.0" prefWidth="100.0" text="名前" > <cellValueFactory><PropertyValueFactory property="name"/></cellValueFactory> </TableColumn> <TableColumn maxWidth="40.0" minWidth="40.0" prefWidth="40.0" text="性別" > <cellValueFactory><PropertyValueFactory property="sex"/></cellValueFactory> </TableColumn> <TableColumn maxWidth="40.0" minWidth="40.0" prefWidth="40.0" text="年齢" > <cellValueFactory><PropertyValueFactory property="age"/></cellValueFactory> </TableColumn> <TableColumn minWidth="120.0" prefWidth="120.0" text="種別" > <cellValueFactory><PropertyValueFactory property="kindCd"/></cellValueFactory> </TableColumn> <TableColumn minWidth="145.0" prefWidth="147.0" text="好物" > <cellValueFactory><PropertyValueFactory property="favorite"/></cellValueFactory> </TableColumn> </columns> <columnResizePolicy> <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" /> </columnResizePolicy> </TableView> </children> </AnchorPane> </code></pre> <h3 id="CSSの修正"><a href="#CSS%E3%81%AE%E4%BF%AE%E6%AD%A3">CSSの修正</a></h3> <p><span class=" author-d-iz88z86z86za0dz67zz78zz78zz74zz68zjz80zz71z9iz90z9z84zrpz78zez89zz80zz74zz70ziz73z2qz76zgqnz66z0uyz82zz89zz78zz83zhjez73zj">application.css<br /> </span></p> <pre><code> /* JavaFX CSS - Leave this comment until you have at least create one rule which uses -fx-Property */ .root { -fx-font-family: "Meiryo"; } </code></pre> <h3 id="TableViewの型を変更"><a href="#TableView%E3%81%AE%E5%9E%8B%E3%82%92%E5%A4%89%E6%9B%B4">TableViewの型を変更</a></h3> <p>SampleController.java</p> <p>クラスに定義されているメンバーの型を新規作成した TblCat に変更します。</p> <pre><code> /** テーブルビュー:一覧 */ @FXML private TableView<TblCat> tvCat; </code></pre> <h3 id="検索ボタンクリック時のメソッドを追加"><a href="#%E6%A4%9C%E7%B4%A2%E3%83%9C%E3%82%BF%E3%83%B3%E3%82%AF%E3%83%AA%E3%83%83%E3%82%AF%E6%99%82%E3%81%AE%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%82%92%E8%BF%BD%E5%8A%A0">検索ボタンクリック時のメソッドを追加</a></h3> <p>SampleController.java(メソッドのみ抜粋)</p> <pre><code> /** * 検索ボタンアクションハンドラー. * * @param ev */ @FXML public void btSearchButtonActionHandle(ActionEvent ev) { try { DBAccess dba = new DBAccess(); ResultSet rs = dba.executeQuery("SELECT * FROM TBLCAT ORDER BY NO"); // 取得したデータを追加 ObservableList<TblCat> list = FXCollections.observableArrayList(); while (rs != null &amp;&amp; rs.next()) { TblCat cat = new TblCat(); cat.setNo(rs.getString("NO")); cat.setName(rs.getString("NAME")); cat.setSex(rs.getString("SEX")); cat.setAge(rs.getString("AGE")); cat.setKindCd(rs.getString("KIND_CD")); cat.setFavorite(rs.getString("FAVORITE")); list.add(cat); } tvCat.setItems(list); } catch (SQLException e) { // TODO 自動生成された catch ブロック e.printStackTrace(); } catch (Exception e) { // TODO 自動生成された catch ブロック e.printStackTrace(); } } </code></pre> <p> </p> <h2 id="起動してみる"><a href="#%E8%B5%B7%E5%8B%95%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B">起動してみる</a></h2> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxTblView003.jpg" alt="検索" /></p> <p>起動後、検索ボタンをクリックします。</p> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxTblView004.jpg" alt="データが表示される" /></p> <p>無事にデータが表示されました。</p> <h2 id="まとめ"><a href="#%E3%81%BE%E3%81%A8%E3%82%81">まとめ</a></h2> <p>とりあえず検索の条件などは無視してデータを表示してみました。</p> <p>次回は、検索条件の設定などをやっていきたいと思います。</p> <p>ではでは。</p> doraxdora tag:crieit.net,2005:PublicArticle/15694 2020-01-24T09:43:29+09:00 2020-01-24T09:43:29+09:00 https://crieit.net/posts/JavaFX-Oracle 【JavaFX】Oracleに接続して取得したデータをコンボボックスに設定する <p>前回に引き続き、JavaFXです。<br /> 今回は Oracle からデータを取得してコンボボックスに設定します。</p> <p>利用するテーブルや Oracle のインストールなどは次の記事を参照してください。</p> <p><a target="_blank" rel="nofollow noopener" href="https://www.doraxdora.com/blog/2017/08/31/post-2304/" target="_blank" rel="noopener noreferrer">Oracle Database Express Edition 11g Release 2 のインストールからテーブル作成まで</a></p> <p>Swing でやったのとほぼほぼ同じです。</p> <p>プログラムは前回のものを流用します。</p> <p><a target="_blank" rel="nofollow noopener" href="https://www.doraxdora.com/blog/2017/09/28/post-2615/" target="_blank" rel="noopener noreferrer">【JavaFX】コンボボックスに項目を設定する</a></p> <h2 id="ライブラリの追加"><a href="#%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%81%AE%E8%BF%BD%E5%8A%A0">ライブラリの追加</a></h2> <p>Oracle からデータを取得するために、ライブラリを追加します。<br /> パッケージ・エクスプローラーからプロジェクトを右クリックし、「プロパティ」を開きます。</p> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxOrclCmb000.jpg" alt="外部 JARの追加" /></p> <p>「Javaのビルドパス」メニューを選択し、「ライブラリー」タブを表示、<br /> 「外部 JAR の追加」ボタンをクリックします。</p> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxOrclCmb001.jpg" alt="外部ファイルの選択" /></p> <p>ファイル選択ダイアログが表示されるので、<br /> Oracle のインストールディレクトリから対象のファイルが格納されているフォルダを開きます。</p> <p>デフォルト(Oracle 11g XE)の場合は次の場所に格納されています。</p> <pre><code>C:\oraclexe\app\oracle\product\11.2.0\server\jdbc\lib </code></pre> <p>ライブラリフォルダーに格納されている「ojdbc6.jar」を開きます。</p> <h2 id="パッケージの作成"><a href="#%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E3%81%AE%E4%BD%9C%E6%88%90">パッケージの作成</a></h2> <p>次の2つのパッケージを作成します。</p> <pre><code>jp.co.doraxdora.common jp.co.doraxdora.dto </code></pre> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxOrclCmb002.jpg" alt="パッケージの作成" /></p> <p>パッケージ・エクスプローラーで「src」を右クリック>「新規」>「パッケージ」を選択します。</p> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxOrclCmb003.jpg" alt="パッケージ名の入力" /></p> <p>名前に「jp.co.doraxdora.common」を入力して「完了」ボタンをクリックします。<br /> 同様に「jp.co.doraxdora.dto」も作成します。</p> <p> </p> <h2 id="クラスの作成"><a href="#%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%AE%E4%BD%9C%E6%88%90">クラスの作成</a></h2> <p>次の2つのクラスをそれぞれのパッケージに作成します。</p> <pre><code>jp.co.doraxdora.common.DBAccess.java jp.co.doraxdora.dto.MstKind.java </code></pre> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxOrclCmb004.jpg" alt="クラスの作成" /></p> <p>パッケージ・エクスプローラーでパッケージを右クリック>「新規」>「クラス」を選択します。</p> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxOrclCmb005.jpg" alt="クラス名の入力" /></p> <p>名前に「DBAccess」を入力して「完了」ボタンをクリックします。<br /> 同様に「MstKind」も作成します。</p> <h3 id="実装"><a href="#%E5%AE%9F%E8%A3%85">実装</a></h3> <p>DBAccess.java</p> <pre><code> package jp.co.doraxdora.common; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.List; import oracle.jdbc.pool.OracleDataSource; /** * @author doraxdora * */ public class DBAccess { private static String DB_URL = "jdbc:oracle:thin:USER01/USER01@localhost:1521:XE"; private static Connection con = null; /** * コンストラクタ. * * @throws Exception */ public DBAccess() throws Exception{ super(); // ドライバのロード OracleDataSource ds = new OracleDataSource(); ds.setURL(DB_URL); // DB接続 con = ds.getConnection(); } /** * SQLを実行して結果を取得します. * @param sql * @return */ public ResultSet executeQuery(String sql) throws Exception { PreparedStatement stmt = con.prepareStatement(sql); return stmt.executeQuery(); } /** * パラメータを指定してSQLを実行し、結果を取得します. * * @param sql * @param param * @return * @throws Exception */ public ResultSet executeQuery(String sql, List<Object> param) throws Exception { PreparedStatement stmt = con.prepareStatement(sql); for (int i = 0; i < param.size(); i++) { if (param.get(i) instanceof String) { stmt.setString(i, (String) param.get(i)); } else if (param.get(i) instanceof Integer) { stmt.setInt(i, (int) param.get(i)); } } return stmt.executeQuery(); } } </code></pre> <p>MstKind.java</p> <pre><code>/** * */ package jp.co.doraxdora.dto; import java.io.Serializable; /** * MST種別クラス. * * @author doraxdora * */ public class MstKind implements Serializable { /** 種別コード */ private String kindCd; /** 種別名 */ private String kindName; /** * @return kindCd */ public String getKindCd() { return kindCd; } /** * @param kindCd セットする kindCd */ public void setKindCd(String kindCd) { this.kindCd = kindCd; } /** * @return kindName */ public String getKindName() { return kindName; } /** * @param kindName セットする kindName */ public void setKindName(String kindName) { this.kindName = kindName; } /* (非 Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return kindName; } } </code></pre> <p>JavaFX の ComboBox の実装が、<br /> toString()メソッドを利用して表示文字列を取得しているため、<br /> toString()メソッドをオーバーライドし、表示するメンバーの値を返すようにする必要があります。</p> <h2 id="コントローラーの修正"><a href="#%E3%82%B3%E3%83%B3%E3%83%88%E3%83%AD%E3%83%BC%E3%83%A9%E3%83%BC%E3%81%AE%E4%BF%AE%E6%AD%A3">コントローラーの修正</a></h2> <h3 id="インポート文の追加およびメンバー型の変更"><a href="#%E3%82%A4%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%88%E6%96%87%E3%81%AE%E8%BF%BD%E5%8A%A0%E3%81%8A%E3%82%88%E3%81%B3%E3%83%A1%E3%83%B3%E3%83%90%E3%83%BC%E5%9E%8B%E3%81%AE%E5%A4%89%E6%9B%B4">インポート文の追加およびメンバー型の変更</a></h3> <p>SampleController.java(変更箇所のみ抜粋)</p> <pre><code>// インポート文追加 import jp.co.doraxdora.common.DBAccess; import jp.co.doraxdora.dto.MstKind; public class SampleController implements Initializable{ /** コンボボックス:名前 */ // String から MstKind のリストに変更 // アクセサも修正が必要 @FXML //private ComboBox<String> cbKind; private ComboBox<MstKind> cbKind; /** * @return cbKind */ public ComboBox<MstKind> getCbKind() { return cbKind; } /** * @param cbKind セットする cbKind */ public void setCbKind(ComboBox<MstKind> cbKind) { this.cbKind = cbKind; } ) </code></pre> <h3 id="初期化処理の修正"><a href="#%E5%88%9D%E6%9C%9F%E5%8C%96%E5%87%A6%E7%90%86%E3%81%AE%E4%BF%AE%E6%AD%A3">初期化処理の修正</a></h3> <p>SampleController.java(メソッドのみ抜粋)</p> <pre><code> /* * (非 Javadoc) * @see javafx.fxml.Initializable#initialize(java.net.URL, java.util.ResourceBundle) */ @Override public void initialize(URL location, ResourceBundle resources) { try { DBAccess dba = new DBAccess(); ResultSet rs = dba.executeQuery("SELECT * FROM MSTKIND ORDER BY KIND_CD"); // 指定なしを追加 MstKind empty = new MstKind(); empty.setKindCd("-1"); empty.setKindName("指定なし"); cbKind.getItems().add(empty); // 取得したデータを追加 while (rs != null &amp;&amp; rs.next()) { MstKind kind = new MstKind(); kind.setKindCd(rs.getString("KIND_CD")); kind.setKindName(rs.getString("KIND_NAME")); cbKind.getItems().add(kind); } // 初期選択状態を設定 cbKind.getSelectionModel().select(0); } catch (SQLException e) { // TODO 自動生成された catch ブロック e.printStackTrace(); } catch (Exception e) { // TODO 自動生成された catch ブロック e.printStackTrace(); } } </code></pre> <h2 id="起動してみる"><a href="#%E8%B5%B7%E5%8B%95%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B">起動してみる</a></h2> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxOrclCmb006.jpg" alt="起動した画面" /></p> <p>内容は前回のものと変わりませんが、<br /> 無事に表示することができました。</p> <p>次回は一覧にデータ表示するところまでやってみたいと思います。</p> <p>ではでは。</p> doraxdora tag:crieit.net,2005:PublicArticle/15691 2020-01-20T09:27:40+09:00 2020-01-20T09:27:40+09:00 https://crieit.net/posts/JavaFX 【JavaFX】さくっとコンボボックスに項目を設定してみる <p>今回は、JavaFXの基本的な構成や仕組みなどを学ぶために<br /> とりあえずコンボボックスに項目を設定して選択できるようにしてみます。</p> <p>開発環境については以前の記事を参考にしてください。<br /> <a target="_blank" rel="nofollow noopener" href="https://www.doraxdora.com/blog/2017/09/22/post-2584/" target="_blank" rel="noopener noreferrer" data-blogcard="1">【JavaFX】eclipseにJavaFXプラグインをインストールして Hello World してみる</a></p> <h2 id="画面の作成"><a href="#%E7%94%BB%E9%9D%A2%E3%81%AE%E4%BD%9C%E6%88%90">画面の作成</a></h2> <p>画面は SceneBuilder にて作成します。<br /> (WPF で作成した画面と同等のものを作ってみたいと思います。)</p> <p><a target="_blank" rel="nofollow noopener" href="https://www.doraxdora.com/blog/category/dev/c/" target="_blank" rel="noopener noreferrer">WPF編はこちら</a></p> <p>初期状態では、「BorderPane」が設置されていますが、<br /> それを削除して「AnchorPane」を設置し、その上にコントロールを配置していきます。</p> <p>AnchorPane というのは、<br /> 簡単に言うとコントロールを座標(絶対位置)で配置できるレイアウトになります。</p> <p>その他のレイアウトについてはそのうち紹介できればします。</p> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxCombo000.jpg" alt="画面へコントロールを配置" /></p> <p>SceneBuilder上で CTRL + P を押すとプレビュー表示することができます。</p> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxCombo001.jpg" alt="プレビュー表示" /></p> <p>eclipse で Java プリケーションの実行をしなくても起動後の画面がサクッと表示されます。<br /> (プログラムは動かないのでコントロールの位置確認のみ)</p> <p>Sample.fxml</p> <pre><code><?xml version="1.0" encoding="UTF-8"?> <?import javafx.scene.control.Button?> <?import javafx.scene.control.ComboBox?> <?import javafx.scene.control.Label?> <?import javafx.scene.control.TableColumn?> <?import javafx.scene.control.TableView?> <?import javafx.scene.control.TextField?> <?import javafx.scene.layout.AnchorPane?> <AnchorPane fx:id="mainPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="350.0" prefWidth="530.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.SampleController"><!-- ← コントローラクラスと紐付 --> <children> <!-- 名前 --> <Label fx:id="lbName" layoutX="14.0" layoutY="33.0" text="名前:" /> <TextField fx:id="txName" layoutX="58.0" layoutY="29.0" /> <!-- 種別 --> <Label fx:id="lbKind" layoutX="221.0" layoutY="33.0" text="種別:" /> <ComboBox fx:id="cbKind" layoutX="264.0" layoutY="29.0" prefWidth="150.0" /> <!-- 検索ボタン --> <Button fx:id="btSearch" layoutX="456.0" layoutY="29.0" mnemonicParsing="false" text="検索" /> <!-- 一覧 --> <TableView fx:id="tvCat" layoutX="14.0" layoutY="63.0" prefHeight="273.0" prefWidth="498.0"> <columns> <TableColumn maxWidth="50.0" minWidth="50.0" prefWidth="50.0" resizable="false" text="No" /> <TableColumn maxWidth="100.0" minWidth="100.0" prefWidth="100.0" text="名前" /> <TableColumn maxWidth="40.0" minWidth="40.0" prefWidth="40.0" text="性別" /> <TableColumn maxWidth="40.0" minWidth="40.0" prefWidth="40.0" text="年齢" /> <TableColumn minWidth="120.0" prefWidth="120.0" text="種別" /> <TableColumn minWidth="145.0" prefWidth="147.0" text="好物" /> </columns> <columnResizePolicy> <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" /> </columnResizePolicy> </TableView> </children> </AnchorPane> </code></pre> <h2 id="コントローラクラスの実装"><a href="#%E3%82%B3%E3%83%B3%E3%83%88%E3%83%AD%E3%83%BC%E3%83%A9%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%AE%E5%AE%9F%E8%A3%85">コントローラクラスの実装</a></h2> <p>JavaFX では、<br /> 基本的に fxml (画面定義)と対応するコントローラー(クラス)を作成し、<br /> コントローラーにてイベントなどを処理するような構成となります。</p> <p>fxml に記述した</p> <pre><code> fx:controller="application.SampleController" </code></pre> <p>でコントローラを指定し、コントローラークラスには<br /> 画面に設置したコントロールをメンバーとして定義することが必要。</p> <p>こうすることで、<br /> コントローラークラスにて画面のコントロールを操作することが可能となります。</p> <p>SampleController.java</p> <pre><code>package application; import java.net.URL; import java.util.ResourceBundle; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.Button; import javafx.scene.control.ComboBox; import javafx.scene.control.Label; import javafx.scene.control.TableView; import javafx.scene.control.TextField; import javafx.scene.layout.AnchorPane; public class SampleController implements Initializable{ /** パネル:メイン */ @FXML private AnchorPane mainPane; /** ラベル:名前 */ @FXML private Label lbName; /** テキストフィールド:名前 */ @FXML private TextField txName; /** ラベル:種別 */ @FXML private Label lbKind; /** コンボボックス:名前 */ @FXML private ComboBox<String> cbKind; /** ボタン:検索 */ @FXML private Button btSearch; /** テーブルビュー:一覧 */ @FXML private TableView<String> tvCat; /** * @return mainPane */ public AnchorPane getMainPane() { return mainPane; } /** * @param mainPane セットする mainPane */ public void setMainPane(AnchorPane mainPane) { this.mainPane = mainPane; } /** * @return lbName */ public Label getLbName() { return lbName; } /** * @param lbName セットする lbName */ public void setLbName(Label lbName) { this.lbName = lbName; } /** * @return txName */ public TextField getTxName() { return txName; } /** * @param txName セットする txName */ public void setTxName(TextField txName) { this.txName = txName; } /** * @return lbKind */ public Label getLbKind() { return lbKind; } /** * @param lbKind セットする lbKind */ public void setLbKind(Label lbKind) { this.lbKind = lbKind; } /** * @return cbKind */ public ComboBox<String> getCbKind() { return cbKind; } /** * @param cbKind セットする cbKind */ public void setCbKind(ComboBox<String> cbKind) { this.cbKind = cbKind; } /** * @return btSearch */ public Button getBtSearch() { return btSearch; } /** * @param btSearch セットする btSearch */ public void setBtSearch(Button btSearch) { this.btSearch = btSearch; } /** * @return tvCat */ public TableView<String> getTvCat() { return tvCat; } /** * @param tvCat セットする tvCat */ public void setTvCat(TableView<String> tvCat) { this.tvCat = tvCat; } /* * (非 Javadoc) * @see javafx.fxml.Initializable#initialize(java.net.URL, java.util.ResourceBundle) */ @Override public void initialize(URL location, ResourceBundle resources) { // コンボボックスに項目を追加 cbKind.getItems().add("指定なし"); cbKind.getItems().add("キジトラ"); cbKind.getItems().add("長毛種(不明)"); cbKind.getItems().add("ミケ(っぽい)"); cbKind.getItems().add("サビ"); cbKind.getItems().add("その他"); // 初期選択状態を設定 cbKind.getSelectionModel().select(0); } } </code></pre> <p> </p> <h2 id="メインクラスの修正"><a href="#%E3%83%A1%E3%82%A4%E3%83%B3%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%AE%E4%BF%AE%E6%AD%A3">メインクラスの修正</a></h2> <p>ちょっと嵌ったんですが<br /> fxml で AnchorPane のサイズをいくら変更しても<br /> いざ起動してみるとその通りのサイズになってくれませんでした。</p> <p>よくよく見なおしてみると、メインクラスにて、<br /> Scene のインスタンスを初期化する際に、幅と高さを指定していたことが原因でした。<br /> (デフォルトでそうなっている)</p> <p>幅と高さの指定を取り除いたら問題なく fxml で指定したサイズで表示されるようになりました。</p> <p>Main.java</p> <pre><code>package application; import javafx.application.Application; import javafx.fxml.FXMLLoader; import javafx.scene.Scene; import javafx.scene.layout.AnchorPane; import javafx.stage.Stage; public class Main extends Application { @Override public void start(Stage primaryStage) { try { AnchorPane root = (AnchorPane) FXMLLoader.load(getClass().getResource("Sample.fxml")); // 幅と高さを指定してインスタンスを生成 //Scene scene = new Scene(root, 400, 400); // 幅と高さを指定せずにインスタンスを生成 Scene scene = new Scene(root); scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm()); primaryStage.setScene(scene); primaryStage.show(); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { launch(args); } } </code></pre> <p> </p> <h2 id="起動してみる"><a href="#%E8%B5%B7%E5%8B%95%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B">起動してみる</a></h2> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxCombo002.jpg" alt="コンボボックスに項目が設定された画面" /></p> <p>とりあえずコンボボックスへ項目を設定することができました。</p> <p>次回は、データベースから取得したデータを設定してみたいと思います。</p> <p>ではでは。</p> <p> </p> doraxdora tag:crieit.net,2005:PublicArticle/15681 2020-01-14T09:57:33+09:00 2020-01-14T10:00:51+09:00 https://crieit.net/posts/JavaFX-eclipse-JavaFX-Hello-World 【JavaFX】eclipseにJavaFXプラグインをインストールして Hello World してみる <p>どうも Swing が廃止される方向のようなので、<br /> 方向転換して Java FX をやってみたいと思います。</p> <p>まずは開発環境の構築から。</p> <h2 id="e(fx)clipseのインストール"><a href="#e%28fx%29clipse%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB">e(fx)clipseのインストール</a></h2> <p>JavaFX 用の eclipse プラグインをインストールします。</p> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxHello000.jpg" alt="新規ソフトウェアのインストール" /></p> <p>上部メニューの「ヘルプ」>「新規ソフトウェアのインストール」を選択します。</p> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxHello001.jpg" alt="インストール画面" /></p> <p>インストール画面が表示されるので、「追加」ボタンをクリックします。</p> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxHello002.jpg" alt="リポジトリ-の追加" /></p> <p>名前、ロケーションにそれぞれ下記を入力します。</p> <pre><code>名前: e(fx)clipse ロケーション: [http://download.eclipse.org/efxclipse/updates-released/2.4.0/site](http://download.eclipse.org/efxclipse/updates-released/2.4.0/site) </code></pre> <p> </p> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxHello003.jpg" alt="インストールモジュールの選択" /></p> <p>インストール画面にて、追加したロケーションを選択し<br /> e(fx)clipse - IDE を選択して「次へ」ボタンをクリックします。</p> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxHello004.jpg" alt="インストール詳細画面" /></p> <p>インストール詳細画面が表示されるので「次へ」ボタンをクリックします。</p> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxHello005.jpg" alt="同意画面" /></p> <p>ライセンス画面が表示されるので、<br /> 使用条件の条項に同意して「完了」ボタンをクリックします。</p> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxHello006.jpg" alt="再起動確認画面" /></p> <p>再起動確認画面が表示されます。<br /> ソフトウェア更新のため再起動が必要となりますので、「はい」ボタンをクリックして eclipse を再起動します。</p> <h2 id="Scene Builder のインストール"><a href="#Scene+Builder+%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB">Scene Builder のインストール</a></h2> <h3 id="ダウンロード"><a href="#%E3%83%80%E3%82%A6%E3%83%B3%E3%83%AD%E3%83%BC%E3%83%89">ダウンロード</a></h3> <p>次のURLにアクセスします。<br /> <a target="_blank" rel="nofollow noopener" href="http://gluonhq.com/products/scene-builder/#download" target="_blank" rel="noopener noreferrer">http://gluonhq.com/products/scene-builder/#download</a></p> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxHello007.jpg" alt="ダウンロードページ" /></p> <p>今回は、 Windows 64bit のインストーラーをダウンロードします。</p> <h3 id="インストール"><a href="#%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB">インストール</a></h3> <p>ダウンロードした「SceneBuilder-8.3.0.exe」を実行します。</p> <p> </p> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxHello008.jpg" alt="セキュリティの警告" /></p> <p>セキュリティの警告が表示される場合は「実行」ボタンをクリックします。</p> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxHello009.jpg" alt="ライセンス同意画面" /></p> <p>ライセンス画面が表示されるので、同意して「Next」ボタンをクリックします。</p> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxHello010.jpg" alt="インストール先指定画面" /></p> <p>インストール先指定画面が表示されるので、<br /> デフォルトのまま「Next」ボタンをクリックします。</p> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxHello011.jpg" alt="Scene Builder" /></p> <p>SceneBuilder の画面が起動しました。</p> <h2 id="JavaFXの設定"><a href="#JavaFX%E3%81%AE%E8%A8%AD%E5%AE%9A">JavaFXの設定</a></h2> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxHello012.jpg" alt="設定画面の表示" /></p> <p>上部メニューの「ウィンドウ」>「設定」を開きます。</p> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxHello013.jpg" alt="Scene Builder のパスを設定" /></p> <p>サイドメニューの「JavaFX」を選択し、<br /> SceneBuilderの実行ファイルまでのパスを設定、「OK」ボタンをクリックします。</p> <p>デフォルトであれば</p> <blockquote>C:\Users\[ユーザ名]\AppData\Local\SceneBuilder\SceneBuilder.exe</blockquote> <p>となります。</p> <h2 id="JavaFX プロジェクトの作成"><a href="#JavaFX+%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AE%E4%BD%9C%E6%88%90">JavaFX プロジェクトの作成</a></h2> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxHello014.jpg" alt="" /></p> <p>パッケージ・エクスプローラー上で右クリックし、「新規」>「その他」を選択します。</p> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxHello015.jpg" alt="プロジェクト作成" /></p> <p>「JavaFX」>「JavaFX Project」を選択し「次へ」ボタンをクリックします。</p> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxHello016.jpg" alt="プロジェクト名の入力" /></p> <p>プロジェクト名を入力し「次へ」ボタンをクリックします。</p> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxHello017.jpg" alt="ビルド設定画面" /></p> <p>Java設定画面が表示されるので「次へ」ボタンをクリックします。</p> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxHello018.jpg" alt="言語設定" /></p> <p>プロジェクト設定画面が表示されるので、言語に「FXML」を選択し、「完了」ボタンをクリックします。</p> <h3 id="Hello World を表示してみる"><a href="#Hello+World+%E3%82%92%E8%A1%A8%E7%A4%BA%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B">Hello World を表示してみる</a></h3> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxHello019.jpg" alt="Scene Builder で開く" /></p> <p>作成したプロジェクト>「src」>「application」>「Sample.fxml」を右クリックし<br /> 「Open with SceneBuilder」を選択します。</p> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxHello020.jpg" alt="Scene Builder 起動" /></p> <p>Sample.fxml ファイルが SceneBuilder で開かれます。</p> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxHello021.jpg" alt="ラベルの配置" /></p> <p>左側メニューの「Control」より、ラベルを選択して画面中央に配置し、<br /> 表示名を「Hello World!」に変更します。</p> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxHello022.jpg" alt="保存" /></p> <p>上部メニューの「File」>「Save」を選択して変更を保存して SceneBuilder を終了します。</p> <h2 id="起動してみる"><a href="#%E8%B5%B7%E5%8B%95%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B">起動してみる</a></h2> <p>プロジェクトを右クリックし「実行」>「Java アプリケーション」を選択します。</p> <p> </p> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/JavaFxHello024.jpg" alt="起動した画面" /></p> <p>無事に表示されました。</p> <h2 id="まとめ"><a href="#%E3%81%BE%E3%81%A8%E3%82%81">まとめ</a></h2> <p>ひとまず今回はここまで。</p> <p>次回からまた色々やっていきたいと思います。</p> <p>ではでは。</p> doraxdora tag:crieit.net,2005:PublicArticle/15669 2020-01-09T09:39:32+09:00 2020-01-09T09:39:32+09:00 https://crieit.net/posts/Swing-Oracle-JTable 【Swing】Oracleに接続して取得したデータをJTable に表示してみる <p>前回に引き続き、Swingを弄ってみます。<br /> 今回はWPFの時と同様、猫の一覧を表示しました。</p> <p>プログラムは前回のものを流用します。<br /> <a target="_blank" rel="nofollow noopener" href="https://www.doraxdora.com/blog/2017/09/09/2400/" target="_blank" rel="noopener noreferrer" data-blogcard="1">【Swing】Oracleに接続して取得したデータをコンボボックスに設定する</a></p> <h2 id="画面の変更"><a href="#%E7%94%BB%E9%9D%A2%E3%81%AE%E5%A4%89%E6%9B%B4">画面の変更</a></h2> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/SwingJTable000.jpg" alt="JTableの配置" /></p> <p>JScrollPane、JTableを配置します。<br /> こうすることにより、JTableにヘッダー、スクロールバーが表示されるようになります。</p> <h2 id="プログラムの修正"><a href="#%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0%E3%81%AE%E4%BF%AE%E6%AD%A3">プログラムの修正</a></h2> <h3 id="新規クラス(インナー)の追加"><a href="#%E6%96%B0%E8%A6%8F%E3%82%AF%E3%83%A9%E3%82%B9%EF%BC%88%E3%82%A4%E3%83%B3%E3%83%8A%E3%83%BC%EF%BC%89%E3%81%AE%E8%BF%BD%E5%8A%A0">新規クラス(インナー)の追加</a></h3> <p>検索ボタン押下時の処理を追加します。<br /> イベントは、「ActionListener」を実装したクラスで行います。</p> <p>MainForm.java</p> <pre><code> /** * 検索ボタンアクションリスナークラス * * @author doraxdora * */ public class SearchActionListener implements ActionListener { /** * イベントハンドラ. * */ @Override public void actionPerformed(ActionEvent event) { if (event.getSource().equals(btnSearch)) { try { // SQL文の組み立て // 画面で指定された条件を組み込む String sql = "SELECT C.NO" + ", C.NAME" + ", C.SEX" + ", C.AGE" + ", K.KIND_NAME" + ", C.FAVORITE" + " FROM TBLCAT C" + " LEFT OUTER JOIN MSTKIND K ON (" + " C.KIND_CD = K.KIND_CD" + " )"; String where = ""; String searchName = txSearchName.getText(); if (searchName != null &amp;&amp; !searchName.equals("")) { where = " WHERE C.NAME LIKE '" + searchName + "%'"; } String searchKind = (String)cbSearchKind.getSelectedItem(); if (searchKind != null &amp;&amp; !searchKind.equals("指定なし")) { if (where != "") { where += " AND"; } else { where += " WHERE"; } where += " K.KIND_NAME = '" + searchKind + "'"; } sql += where; sql += " ORDER BY C.NO"; // データを取得し、JTableにセットする TableModel の形に編集 DBAccess dba = new DBAccess(); ResultSet rs = dba.executeQuery(sql); List<String[]> list = new ArrayList<String[]>(); while (rs != null &amp;&amp; rs.next()) { String[] items = new String[6]; items[0] = rs.getString("NO"); items[1] = rs.getString("NAME"); items[2] = rs.getString("SEX"); items[3] = rs.getString("AGE"); items[4] = rs.getString("KIND_NAME"); items[5] = rs.getString("FAVORITE"); list.add(items); } // JTable にセット String[] columnHeader = {"No","名前","性別","年齢","種別","好物"}; DefaultTableModel tm = new DefaultTableModel((String[][])list.toArray(new String[0][0]), columnHeader); tblCatData.setModel(tm); } catch (Exception e) { System.out.println(e.getMessage()); } } } } </code></pre> <p> </p> <h3 id="初期化処理の修正"><a href="#%E5%88%9D%E6%9C%9F%E5%8C%96%E5%87%A6%E7%90%86%E3%81%AE%E4%BF%AE%E6%AD%A3">初期化処理の修正</a></h3> <p>少し整理しました。<br /> あとは検索ボタン押下時の処理に、作成したクラスをセットします。</p> <p>MainForm.java</p> <pre><code> /** * 画面生成処理. * */ public MainForm() { // メインパネル setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setBounds(100, 100, 480, 304); mainPanel = new JPanel(); mainPanel.setBorder(new EmptyBorder(5, 5, 5, 5)); setContentPane(mainPanel); mainPanel.setLayout(new BorderLayout(0, 0)); JPanel panel = new JPanel(); FlowLayout flowLayout = (FlowLayout) panel.getLayout(); flowLayout.setHgap(10); flowLayout.setAlignment(FlowLayout.LEFT); mainPanel.add(panel, BorderLayout.NORTH); // 名前ラベル JLabel label = new JLabel("名前:"); panel.add(label); // 名前テキストボックス txSearchName = new JTextField(); panel.add(txSearchName); txSearchName.setColumns(10); // 種別ラベル JLabel label_1 = new JLabel("種別:"); panel.add(label_1); // 種別コンボボックス cbSearchKind = new JComboBox<String>(); cbSearchKind.setMinimumSize(new Dimension(100, 19)); cbSearchKind.setBounds(195, 7, 119, 19); panel.add(cbSearchKind); // 検索ボタン btnSearch = new JButton("検索"); // 検索ボタン押下時の処理としてインナークラスをセット btnSearch.addActionListener(new SearchActionListener()); panel.add(btnSearch); // スクロールパネル // JTableは基本スクロールパネルとセットで使用する // 縦横のスクロールおよび、ヘッダが表示されるようになります。 scrollPane = new JScrollPane(); mainPanel.add(scrollPane, BorderLayout.CENTER); // 猫一覧テーブル tblCatData = new JTable(); tblCatData.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); scrollPane.setViewportView(tblCatData); } </code></pre> <p> </p> <h2 id="起動してみる"><a href="#%E8%B5%B7%E5%8B%95%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B">起動してみる</a></h2> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/SwingJTable001.jpg" alt="起動後画面" /></p> <p>起動後の画面です。</p> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/SwingJTable002.jpg" alt="検索結果1" /></p> <p>検索条件を指定しないで検索した結果。</p> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/SwingJTable003.jpg" alt="検索結果2" /></p> <p>名前を指定して検索した結果。</p> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/SwingJTable004.jpg" alt="検索結果3" /></p> <p>種別を指定して検索した結果。</p> <h2 id="まとめ"><a href="#%E3%81%BE%E3%81%A8%E3%82%81">まとめ</a></h2> <p>Swingはひとまずここまでにするかもしれません。<br /> 次回は Java FX なんかもやってみようかなーとか思っています。</p> <p>ではでは。</p> doraxdora tag:crieit.net,2005:PublicArticle/15668 2020-01-09T09:37:13+09:00 2020-01-09T09:37:13+09:00 https://crieit.net/posts/Swing-Oracle 【Swing】Oracleに接続して取得したデータをコンボボックスに設定する <p>今回は、Oracleからデータを取得してコンボボックスに設定してみたいと思います。</p> <p>プロジェクトは前回のものを流用します。</p> <p><a target="_blank" rel="nofollow noopener" href="https://www.doraxdora.com/blog/2017/08/26/post-2203/" target="_blank" rel="noopener noreferrer">【Swing】eclipse に Swingプラグインをインストールして Hello Wolrd してみる</a></p> <h2 id="新規クラスの追加"><a href="#%E6%96%B0%E8%A6%8F%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%AE%E8%BF%BD%E5%8A%A0">新規クラスの追加</a></h2> <h3 id="パッケージの作成"><a href="#%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E3%81%AE%E4%BD%9C%E6%88%90">パッケージの作成</a></h3> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/SwingCombo000.jpg" alt="" /><br /> パッケージ・エクスプローラーから「src」を右クリックし、「新規」>「パッケージ」を選択します。</p> <div class="mceTemp"></div> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/SwingCombo001.jpg" alt="パッケージ作成" /></p> <p>パッケージ名を入力し、「完了」ボタンをクリックします。<br /> 今回は「jp.co.doraxdora.common」としました。</p> <h3 id="クラスの作成"><a href="#%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%AE%E4%BD%9C%E6%88%90">クラスの作成</a></h3> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/SwingCombo002.jpg" alt="クラス作成" /></p> <p>パッケージ・エクスプローラーから作成したパッケージを右クリックし、<br /> 「新規」>「クラス」を選択します。</p> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/SwingCombo003.jpg" alt="クラス作成" /></p> <p>クラス名を入力し、「完了」ボタンをクリックします。<br /> 今回は「DBAccess」としました。</p> <p>DBAccess.java</p> <pre><code> /** * */ package jp.co.doraxdora.common; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.List; import oracle.jdbc.pool.OracleDataSource; /** * @author doraxdora * */ public class DBAccess { private static String DB_URL = "jdbc:oracle:thin:USER01/USER01@localhost:1521:XE"; private static Connection con = null; /** * コンストラクタ. * * @throws Exception */ public DBAccess() throws Exception{ super(); // ドライバのロード OracleDataSource ds = new OracleDataSource(); ds.setURL(DB_URL); // DB接続 con = ds.getConnection(); } /** * SQLを実行して結果を取得します. * @param sql * @return */ public ResultSet executeQuery(String sql) throws Exception { PreparedStatement stmt = con.prepareStatement(sql); return stmt.executeQuery(); } /** * パラメータを指定してSQLを実行し、結果を取得します. * * @param sql * @param param * @return * @throws Exception */ public ResultSet executeQuery(String sql, List<Object> param) throws Exception { PreparedStatement stmt = con.prepareStatement(sql); for (int i = 0; i < param.size(); i++) { if (param.get(i) instanceof String) { stmt.setString(i, (String) param.get(i)); } else if (param.get(i) instanceof Integer) { stmt.setInt(i, (int) param.get(i)); } } return stmt.executeQuery(); } } </code></pre> <p> </p> <h3 id="ライブラリの追加"><a href="#%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%81%AE%E8%BF%BD%E5%8A%A0">ライブラリの追加</a></h3> <p>Oracle からデータを取得するために、ライブラリを追加します。<br /> パッケージ・エクスプローラーからプロジェクトを右クリックし、「プロパティ」を開きます。</p> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/SwingCombo004.jpg" alt="Javaのビルドパス" /></p> <p>「Javaのビルドパス」メニューを選択し、「ライブラリー」タブを表示、<br /> 「外部 JAR の追加」ボタンをクリックします。</p> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/SwingCombo005.jpg" alt="ファイル選択" /></p> <p>ファイル選択ダイアログが表示されるので、<br /> Oracle のインストールディレクトリから対象のファイルが格納されているフォルダを開きます。</p> <p>デフォルト(Oracle 11g XE)の場合は次の場所に格納されています。</p> <p>C:\oraclexe\app\oracle\product\11.2.0\server\jdbc\lib</p> <p>ライブラリフォルダーに格納されている「ojdbc6.jar」を開きます。</p> <p> </p> <h3 id="画面の修正"><a href="#%E7%94%BB%E9%9D%A2%E3%81%AE%E4%BF%AE%E6%AD%A3">画面の修正</a></h3> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/SwingCombo006.jpg" alt="コンポーネントの配置" /></p> <p>WPFの時と同様に、名前ラベル・テキスト、種別ラベル・コンボボックス、ボタンを配置します。</p> <h3 id="プログラムの修正"><a href="#%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0%E3%81%AE%E4%BF%AE%E6%AD%A3">プログラムの修正</a></h3> <p>初期表示時に、<br /> Oracle からデータを取得してコンボボックスに設定するように修正します。</p> <p>MainForm.java</p> <pre><code> package jp.co.doraxdora.form; import java.awt.EventQueue; import java.sql.ResultSet; import java.util.ArrayList; import java.util.List; import javax.swing.DefaultComboBoxModel; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.border.EmptyBorder; import jp.co.doraxdora.common.DBAccess; public class MainForm extends JFrame { private JPanel contentPane; private JTextField textField; private JComboBox<String> comboBox; /** * Launch the application. */ public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { try { MainForm frame = new MainForm(); // コンポーネント初期化 frame.initializeComponent(); frame.setVisible(true); } catch (Exception e) { e.printStackTrace(); } } }); } /** * Create the frame. */ public MainForm() { setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setBounds(100, 100, 462, 304); contentPane = new JPanel(); contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); setContentPane(contentPane); contentPane.setLayout(null); JLabel label = new JLabel("名前:"); label.setBounds(12, 10, 46, 13); contentPane.add(label); textField = new JTextField(); textField.setBounds(47, 7, 96, 19); contentPane.add(textField); textField.setColumns(10); JLabel label_1 = new JLabel("種別:"); label_1.setBounds(159, 10, 56, 13); contentPane.add(label_1); comboBox = new JComboBox(); comboBox.setBounds(195, 7, 119, 19); contentPane.add(comboBox); JButton btnNewButton = new JButton("検索"); btnNewButton.setBounds(345, 6, 91, 21); contentPane.add(btnNewButton); } /** * コンポーネントの初期化処理. * */ private void initializeComponent() throws Exception { DBAccess dba = new DBAccess(); ResultSet rs = dba.executeQuery("SELECT * FROM MSTKIND ORDER BY KIND_CD"); List<String> list = new ArrayList<String>(); while (rs != null &amp;&amp; rs.next()) { list.add((rs.getString("KIND_NAME"))); } DefaultComboBoxModel<String> model = new DefaultComboBoxModel<String>((String[])list.toArray(new String[0])); comboBox.setModel(model); } } </code></pre> <p> </p> <h2 id="起動してみる"><a href="#%E8%B5%B7%E5%8B%95%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B">起動してみる</a></h2> <p><img src="https://www.doraxdora.com/wp-content/uploads/2017/09/SwingCombo007.jpg" alt="画面" /></p> <p>無事にデータを設定することができました。</p> <h2 id="まとめ"><a href="#%E3%81%BE%E3%81%A8%E3%82%81">まとめ</a></h2> <p>ひとまずデータの取得まではできたので、<br /> 次回はデータテーブルを設置して、検索したデータを表示するところをやろうかと思います。</p> <p>ではでは。</p> <p> </p> doraxdora tag:crieit.net,2005:PublicArticle/14699 2018-12-28T18:23:36+09:00 2019-04-24T12:20:09+09:00 https://crieit.net/posts/d3d77d9486d912d99dc82e71f0defa04 これまで作ったものまとめ <h1 id="開発継続中"><a href="#%E9%96%8B%E7%99%BA%E7%B6%99%E7%B6%9A%E4%B8%AD">開発継続中</a></h1> <h2 id="アニメ視聴遅れ管理サービス"><a href="#%E3%82%A2%E3%83%8B%E3%83%A1%E8%A6%96%E8%81%B4%E9%81%85%E3%82%8C%E7%AE%A1%E7%90%86%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9">アニメ視聴遅れ管理サービス</a></h2> <p><a target="_blank" rel="nofollow noopener" href="https://annict-access.herokuapp.com">リンク</a><br /> <a href="https://crieit.now.sh/upload_images/e6703cda3f4a1b93dc92eeda163b0a1e5c25f199a6fce.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e6703cda3f4a1b93dc92eeda163b0a1e5c25f199a6fce.jpg?mw=700" alt="image" /></a><br /> <a target="_blank" rel="nofollow noopener" href="https://annict.jp/">アニメ視聴管理サービス「Annict」</a>が提供している外部APIを使ったWEBサービス。視聴状況の遅れなどを可視化したもの。</p> <ul> <li>開発・保守期間: 2016.11~ <ul> <li>サーバサイド:Java → NodeJS</li> <li>フロントエンド:AngularJS → ReactJS</li> <li>Heroku</li> <li>Netlify</li> <li>OAuth(twitter/annict API)</li> <li>DBなし</li> </ul></li> </ul> <h2 id="カラオケ機種間横断検索サービス"><a href="#%E3%82%AB%E3%83%A9%E3%82%AA%E3%82%B1%E6%A9%9F%E7%A8%AE%E9%96%93%E6%A8%AA%E6%96%AD%E6%A4%9C%E7%B4%A2%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9">カラオケ機種間横断検索サービス</a></h2> <p><a href="https://crieit.now.sh/upload_images/b08b548caec5795ad40e35af9c85f4c95c8e36cc39239.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/b08b548caec5795ad40e35af9c85f4c95c8e36cc39239.png?mw=700" alt="image" /></a><br /> <a target="_blank" rel="nofollow noopener" href="https://karaoke-db.herokuapp.com/">リンク</a></p> <ul> <li>開発・保守期間: 2018.11~</li> <li>言語 <ul> <li>ReactJS</li> <li>NodeJS</li> <li>SQLite3</li> <li>Heroku</li> </ul></li> </ul> <h2 id="野球リーグスコア管理システム ver 2"><a href="#%E9%87%8E%E7%90%83%E3%83%AA%E3%83%BC%E3%82%B0%E3%82%B9%E3%82%B3%E3%82%A2%E7%AE%A1%E7%90%86%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0+ver+2">野球リーグスコア管理システム ver 2</a></h2> <p><a href="https://crieit.now.sh/upload_images/b08b548caec5795ad40e35af9c85f4c95c2b275b54dce.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/b08b548caec5795ad40e35af9c85f4c95c2b275b54dce.png?mw=700" alt="image" /></a><br /> <a target="_blank" rel="nofollow noopener" href="https://jcblscore-react.netlify.com/">リンク</a></p> <ul> <li>開発・保守期間: 2018.07~</li> <li>言語 <ul> <li>NodeJS</li> <li>ReactJS</li> <li>MySQL</li> <li>Netlify</li> </ul></li> </ul> <h2 id="カラオケ成績管理ツール"><a href="#%E3%82%AB%E3%83%A9%E3%82%AA%E3%82%B1%E6%88%90%E7%B8%BE%E7%AE%A1%E7%90%86%E3%83%84%E3%83%BC%E3%83%AB">カラオケ成績管理ツール</a></h2> <p><a href="https://crieit.now.sh/upload_images/d43b2b4ff3155c9ac692bd97af4bfaca5c25f1dbeacb7.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/d43b2b4ff3155c9ac692bd97af4bfaca5c25f1dbeacb7.jpg?mw=700" alt="image" /></a></p> <ul> <li>開発・保守期間: 2018.07~</li> <li>言語 <ul> <li>ReactJS</li> <li>Chart.js</li> </ul></li> </ul> <h2 id="slack流量計 ver 2"><a href="#slack%E6%B5%81%E9%87%8F%E8%A8%88+ver+2">slack流量計 ver 2</a></h2> <p><a href="https://crieit.now.sh/upload_images/cde17b25ca69bea2456237d0a6533a4d5c27756aa0223.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/cde17b25ca69bea2456237d0a6533a4d5c27756aa0223.jpg?mw=700" alt="image" /></a><br /> <a href="https://crieit.net/posts/slack">slack流量計をバージョンアップしました。</a></p> <ul> <li>開発・保守期間: 2018.11~</li> <li>言語 <ul> <li>NodeJS</li> <li>chart.js</li> <li>puppeteer</li> <li>MySQL</li> </ul></li> </ul> <h2 id="Amazon Alexaスキル"><a href="#Amazon+Alexa%E3%82%B9%E3%82%AD%E3%83%AB">Amazon Alexaスキル</a></h2> <ul> <li>開発・保守期間: 2018.12~</li> <li>言語 <ul> <li>NodeJS</li> <li>Heroku</li> </ul></li> </ul> <h1 id="開発終了"><a href="#%E9%96%8B%E7%99%BA%E7%B5%82%E4%BA%86">開発終了</a></h1> <h2 id="DAMランキングバトル・月別成績推移表出力ツール"><a href="#DAM%E3%83%A9%E3%83%B3%E3%82%AD%E3%83%B3%E3%82%B0%E3%83%90%E3%83%88%E3%83%AB%E3%83%BB%E6%9C%88%E5%88%A5%E6%88%90%E7%B8%BE%E6%8E%A8%E7%A7%BB%E8%A1%A8%E5%87%BA%E5%8A%9B%E3%83%84%E3%83%BC%E3%83%AB">DAMランキングバトル・月別成績推移表出力ツール</a></h2> <p>DAMランキングバトル成績取得ツール「らんばと君」の一ヶ月ごとの出力データを元に得点・順位の推移をHTML化するプログラム。</p> <ul> <li>開発・保守期間: 2006.07~2013.07(開発終了) <ul> <li>バグ修正</li> <li>ユーザサポート</li> <li>機能追加</li> </ul></li> <li>言語: <ul> <li><a target="_blank" rel="nofollow noopener" href="http://hsp.tv/">Hot Soup Processor 3</a></li> <li>javascript</li> <li>HTML</li> </ul></li> </ul> <h2 id="DAM精密採点・成績集計ツール"><a href="#DAM%E7%B2%BE%E5%AF%86%E6%8E%A1%E7%82%B9%E3%83%BB%E6%88%90%E7%B8%BE%E9%9B%86%E8%A8%88%E3%83%84%E3%83%BC%E3%83%AB">DAM精密採点・成績集計ツール</a></h2> <p>精密採点の結果をHTML/XMLから取得してHTML化するプログラム。<br /> HSPではDOMやHTMLパーサーなどが使えなかったので文字列探索の力<br /> 技でテキスト解析を行っていた。<br /> 現在は代替となるWEBサービスが存在するので開発終了。</p> <ul> <li>開発・保守期間: 2006.09~2015.01(開発終了) <ul> <li>バグ修正</li> <li>ユーザサポート</li> <li>機能追加</li> </ul></li> <li>言語 <ul> <li><a target="_blank" rel="nofollow noopener" href="http://hsp.tv/">Hot Soup Processor 3</a></li> <li>javascript</li> <li>HTML</li> <li>Google Chart API</li> </ul></li> </ul> <h2 id="野球リーグスコア管理システム"><a href="#%E9%87%8E%E7%90%83%E3%83%AA%E3%83%BC%E3%82%B0%E3%82%B9%E3%82%B3%E3%82%A2%E7%AE%A1%E7%90%86%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0">野球リーグスコア管理システム</a></h2> <p>趣味で行っている野球のリーグスコア管理システムを製作。<br /> <a href="https://crieit.net/posts/0431d68ab5279f65797e41cfc18a0eb5">アドベントカレンダー</a>に記事を書いたのでそちらを参照のこと。</p> <ul> <li>開発・保守期間: 2015.06~2018.12(開発終了) <ul> <li>DB設計(改修後14テーブル)</li> <li>基本設計</li> <li>実装</li> <li>運用・改修</li> </ul></li> </ul> <h2 id="読書管理アプリデータ移行サービス"><a href="#%E8%AA%AD%E6%9B%B8%E7%AE%A1%E7%90%86%E3%82%A2%E3%83%97%E3%83%AA%E3%83%87%E3%83%BC%E3%82%BF%E7%A7%BB%E8%A1%8C%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9">読書管理アプリデータ移行サービス</a></h2> <p>読書管理アプリから別の読書管理アプリにデータ(CSV)を抽出するWEBアプリ。</p> <ul> <li>開発・保守期間: 2016.12~2018.02(開発終了)</li> <li>言語 <ul> <li>Java</li> <li>Seasar2</li> <li>REST</li> <li>DBなし</li> </ul></li> </ul> <h2 id="カラオケ全国採点データ閲覧システム"><a href="#%E3%82%AB%E3%83%A9%E3%82%AA%E3%82%B1%E5%85%A8%E5%9B%BD%E6%8E%A1%E7%82%B9%E3%83%87%E3%83%BC%E3%82%BF%E9%96%B2%E8%A6%A7%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0">カラオケ全国採点データ閲覧システム</a></h2> <ul> <li>開発・保守期間: 2017.02~2017.04(開発終了) <ul> <li>DB設計(4テーブル)</li> <li>基本設計</li> <li>実装(APサーバ/DBサーバ)</li> <li>DBチューニング</li> </ul></li> <li>言語 <ul> <li>Java</li> <li>Seasar2</li> <li>REST</li> <li>MySQL</li> </ul></li> </ul> <h2 id="画像判別アプリ"><a href="#%E7%94%BB%E5%83%8F%E5%88%A4%E5%88%A5%E3%82%A2%E3%83%97%E3%83%AA">画像判別アプリ</a></h2> <p>画像をTensorFlowを用いて機械学習で5つのクラスに分類し、twitterのリプライに添付された画像がどのクラスに分類されるかを判定するプログラム。</p> <ul> <li>開発・保守期間: 2017.05~2017.05(開発終了)</li> <li>言語 <ul> <li>Python 3.5</li> <li>tweepy</li> <li>OpenCV3</li> </ul></li> </ul> <h2 id="ランキングバトル集計ツール"><a href="#%E3%83%A9%E3%83%B3%E3%82%AD%E3%83%B3%E3%82%B0%E3%83%90%E3%83%88%E3%83%AB%E9%9B%86%E8%A8%88%E3%83%84%E3%83%BC%E3%83%AB">ランキングバトル集計ツール</a></h2> <p><a href="https://crieit.now.sh/upload_images/89d3053539e0291cb8a8cd023efdb96f5c25f2744cc13.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/89d3053539e0291cb8a8cd023efdb96f5c25f2744cc13.jpg?mw=700" alt="image" /></a><br /> 2006年7月から提供しているDAMランキングバトル集計ツールのデータをWEB上で保存・閲覧できるサービス。</p> <ul> <li>開発・保守期間: 2018.02~2018.04(開発中止)</li> <li>言語 <ul> <li>Java</li> <li>Spring MVC</li> <li>MIrageSQL</li> <li>MySQL</li> <li>materializeCSS</li> <li>OAuth</li> </ul></li> </ul> <h2 id="「精密集計」複数アカウント統合ツール"><a href="#%E3%80%8C%E7%B2%BE%E5%AF%86%E9%9B%86%E8%A8%88%E3%80%8D%E8%A4%87%E6%95%B0%E3%82%A2%E3%82%AB%E3%82%A6%E3%83%B3%E3%83%88%E7%B5%B1%E5%90%88%E3%83%84%E3%83%BC%E3%83%AB">「精密集計」複数アカウント統合ツール</a></h2> <p>カラオケ成績集計サービス「精密集計」のデータをCSVでインポートし、<br /> JSONでエクスポートするWEBアプリ。</p> <ul> <li>開発・保守期間: 2017.09~</li> <li>言語 <ul> <li>Java</li> <li>Seasar2</li> <li>AngularJS</li> <li>MySQL</li> </ul></li> </ul> <h2 id="GoogleHomeにしゃべらせるアプリ"><a href="#GoogleHome%E3%81%AB%E3%81%97%E3%82%83%E3%81%B9%E3%82%89%E3%81%9B%E3%82%8B%E3%82%A2%E3%83%97%E3%83%AA">GoogleHomeにしゃべらせるアプリ</a></h2> <p>Google-Home-NotifierやTTS APIを使わない実装</p> <ul> <li>開発・保守期間: 2018.03</li> <li>言語 <ul> <li>NodeJS</li> </ul></li> </ul> <h2 id="slack流量計"><a href="#slack%E6%B5%81%E9%87%8F%E8%A8%88">slack流量計</a></h2> <ul> <li>開発・保守期間: 2018.04~2018.12</li> <li>言語 <ul> <li>Java</li> <li>Spring MVC</li> <li>MySQL</li> </ul></li> </ul> ckoshien tag:crieit.net,2005:PublicArticle/14617 2018-12-01T18:54:37+09:00 2019-05-07T21:05:19+09:00 https://crieit.net/posts/0431d68ab5279f65797e41cfc18a0eb5 野球リーグスコア管理システムに用いている技術について <p>この記事は<a href="https://crieit.net/advent-calendars/2018/technology">「個人開発サービスに用いられている技術 Advent Calendar 2018」</a>8日目の記事です。昨日は<a href="https://crieit.net/users/hrz31">Hiroz</a>さんの「Koretteのオモテとウラ」という記事でした。<br /> 今回は個人で開発している野球リーグスコア管理システムの現行の技術と開発中システムの技術について触れようと思います。</p> <h1 id="野球リーグスコア管理システム(旧)について"><a href="#%E9%87%8E%E7%90%83%E3%83%AA%E3%83%BC%E3%82%B0%E3%82%B9%E3%82%B3%E3%82%A2%E7%AE%A1%E7%90%86%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%28%E6%97%A7%29%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6">野球リーグスコア管理システム(旧)について</a></h1> <p>※旧システムは2018年12月で新システムへと移行しました。</p> <p><a href="https://crieit.now.sh/upload_images/b08b548caec5795ad40e35af9c85f4c95c0241635d9a3.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/b08b548caec5795ad40e35af9c85f4c95c0241635d9a3.png?mw=700" alt="image" /></a></p> <p><a target="_blank" rel="nofollow noopener" href="http://jcbldata.fc2web.com/cbl_index.html">日本カラーボール野球連盟</a>に選手成績の速報を提供しているCMSです。<br /> 2015年6月から開発・運用・新機能追加などを行っています。</p> <h1 id="機能紹介"><a href="#%E6%A9%9F%E8%83%BD%E7%B4%B9%E4%BB%8B">機能紹介</a></h1> <h2 id="シーズン成績ページ(2015/07/02~)"><a href="#%E3%82%B7%E3%83%BC%E3%82%BA%E3%83%B3%E6%88%90%E7%B8%BE%E3%83%9A%E3%83%BC%E3%82%B8%282015%2F07%2F02%EF%BD%9E%29">シーズン成績ページ(2015/07/02~)</a></h2> <p>野球リーグの公式ページからフレームの中で呼び出されるページです。<br /> チーム順位以外は上位10位までを表示する仕様です。</p> <ul> <li>チーム順位</li> <li>個人打撃部門 <ul> <li>打率</li> <li>安打数</li> <li>HR</li> <li>打点</li> </ul></li> <li>個人投手部門 <ul> <li>防御率</li> <li>勝利数</li> <li>セーブ</li> <li>奪三振数</li> </ul></li> <li>個人ノンタイトル部門 <ul> <li>出塁率</li> <li>最多二塁打</li> <li>長打率</li> <li>最多四球</li> <li>OPS</li> </ul></li> </ul> <h2 id="選手個人ページ(2015/07/22~)"><a href="#%E9%81%B8%E6%89%8B%E5%80%8B%E4%BA%BA%E3%83%9A%E3%83%BC%E3%82%B8%282015%2F07%2F22%EF%BD%9E%29">選手個人ページ(2015/07/22~)</a></h2> <p><a href="https://crieit.now.sh/upload_images/e40e06bce35e3ceca1be5b957251e7805c024cbc9d874.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e40e06bce35e3ceca1be5b957251e7805c024cbc9d874.png?mw=700" alt="image" /></a><br /> - 選手紹介(所属チーム、獲得タイトル概要など)<br /> - 打撃成績(期毎、対球団別)<br /> - 投球成績(期毎、対球団別)<br /> - 上記をグラフにしたもの<br /> グラフはhighchartsを使って実装しています。</p> <h2 id="チームページ(2018/10/08~)"><a href="#%E3%83%81%E3%83%BC%E3%83%A0%E3%83%9A%E3%83%BC%E3%82%B8%282018%2F10%2F08%EF%BD%9E%29">チームページ(2018/10/08~)</a></h2> <p><a href="https://crieit.now.sh/upload_images/7f39cb0c5dca52ee81244c660d3c2ff65c024aef4da4a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/7f39cb0c5dca52ee81244c660d3c2ff65c024aef4da4a.png?mw=700" alt="image" /></a><br /> - シーズンごとの通算成績<br /> - 直近の勝敗結果<br /> - チームメンバー通算打撃成績<br /> - チームメンバー通算投球成績</p> <h2 id="統計ページ"><a href="#%E7%B5%B1%E8%A8%88%E3%83%9A%E3%83%BC%E3%82%B8">統計ページ</a></h2> <p><a href="https://crieit.now.sh/upload_images/dff46e83372943224665879750da16be5c024fc71a26e.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/dff46e83372943224665879750da16be5c024fc71a26e.png?mw=700" alt="image" /></a><br /> シーズンごと、チーム別の参加者推移グラフをhighchartsを使って実装しています。</p> <h2 id="全期間通算成績ページ"><a href="#%E5%85%A8%E6%9C%9F%E9%96%93%E9%80%9A%E7%AE%97%E6%88%90%E7%B8%BE%E3%83%9A%E3%83%BC%E3%82%B8">全期間通算成績ページ</a></h2> <p>リーグ自体は2004年から開催されており、成績データは2010年~2018年まで入力されています。レイアウトはシーズン成績ページと同じです。</p> <h2 id="継続参加者成績ページ"><a href="#%E7%B6%99%E7%B6%9A%E5%8F%82%E5%8A%A0%E8%80%85%E6%88%90%E7%B8%BE%E3%83%9A%E3%83%BC%E3%82%B8">継続参加者成績ページ</a></h2> <p>直近2年間に参加している参加者の成績一覧を表示します。<br /> レイアウトはシーズン成績ページと同じです。</p> <h2 id="slack/pushbullet(push通知)連携"><a href="#slack%2Fpushbullet%28push%E9%80%9A%E7%9F%A5%29%E9%80%A3%E6%90%BA">slack/pushbullet(push通知)連携</a></h2> <p>slackとpushbulletというアプリベースのpush通知機能があります。<br /> - 長期天気予報取得<br /> - 日程調整</p> <h1 id="野球リーグスコア管理システムで用いている技術"><a href="#%E9%87%8E%E7%90%83%E3%83%AA%E3%83%BC%E3%82%B0%E3%82%B9%E3%82%B3%E3%82%A2%E7%AE%A1%E7%90%86%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%81%A7%E7%94%A8%E3%81%84%E3%81%A6%E3%81%84%E3%82%8B%E6%8A%80%E8%A1%93">野球リーグスコア管理システムで用いている技術</a></h1> <h2 id="バックエンド(旧)"><a href="#%E3%83%90%E3%83%83%E3%82%AF%E3%82%A8%E3%83%B3%E3%83%89%28%E6%97%A7%29">バックエンド(旧)</a></h2> <ul> <li>言語:Java 8</li> <li>FW: SAStruts(運用続けている間にEOLになりましたorz)</li> <li>DB: MySQL 5.7</li> <li>APサーバ:Tomcat</li> <li>WEBサーバ: Nginx</li> </ul> <h2 id="フロントエンド(旧)"><a href="#%E3%83%95%E3%83%AD%E3%83%B3%E3%83%88%E3%82%A8%E3%83%B3%E3%83%89%28%E6%97%A7%29">フロントエンド(旧)</a></h2> <p>基本ベースは未だにJSPとなっていますが、<br /> バックエンドのAPI化とクライアントレンダリングを進めており、ajax+datatablesで動的に生成しています。<br /> jQuery/<a target="_blank" rel="nofollow noopener" href="https://materializecss.com/">materialize</a>/<a target="_blank" rel="nofollow noopener" href="https://datatables.net/">datatables</a><br /> フォントはNoto Sans Japaneseをメインに、数字部分を Robotoフォントにしています。</p> <hr /> <p>ここまでが現行のシステムとなります。実際に試合後にデータを入力すると成績に反映される仕組みになっています。<br /> ただ、SAStrutsはEOLな技術になっているため、現在モダンな開発環境でリニューアル中です。<br /> ここからは開発中のシステムについての話になります。</p> <h1 id="野球リーグスコア管理システム(現行)について"><a href="#%E9%87%8E%E7%90%83%E3%83%AA%E3%83%BC%E3%82%B0%E3%82%B9%E3%82%B3%E3%82%A2%E7%AE%A1%E7%90%86%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%28%E7%8F%BE%E8%A1%8C%29%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6">野球リーグスコア管理システム(現行)について</a></h1> <h2 id="バックエンド(現行)"><a href="#%E3%83%90%E3%83%83%E3%82%AF%E3%82%A8%E3%83%B3%E3%83%89%28%E7%8F%BE%E8%A1%8C%29">バックエンド(現行)</a></h2> <ul> <li>TypeScript/NodeJS/Express</li> <li>DB: MySQL 5.7</li> </ul> <h2 id="フロントエンド(現行)"><a href="#%E3%83%95%E3%83%AD%E3%83%B3%E3%83%88%E3%82%A8%E3%83%B3%E3%83%89%28%E7%8F%BE%E8%A1%8C%29">フロントエンド(現行)</a></h2> <p>デザインは引き続きmaterializeをベースに、コンポーネントは使い勝手のよいreact-bootstrapを使用しています。<br /> - ReactJS<br /> 使用しているコンポーネント・ライブラリ<br /> - <a target="_blank" rel="nofollow noopener" href="https://react-bootstrap.github.io/">react-bootstrap</a><br /> - <a target="_blank" rel="nofollow noopener" href="https://react-bootstrap-table.github.io/react-bootstrap-table2/">react-bootstrap-table-next</a><br /> - <a target="_blank" rel="nofollow noopener" href="https://www.chartjs.org/">chart.js</a><br /> - materialize<br /> - Netlify</p> <h2 id="現行にしかない機能"><a href="#%E7%8F%BE%E8%A1%8C%E3%81%AB%E3%81%97%E3%81%8B%E3%81%AA%E3%81%84%E6%A9%9F%E8%83%BD">現行にしかない機能</a></h2> <p>旧システムにはないこれらの機能を実装しています。<br /> 新機能は思いついたところから実装していっています。</p> <h3 id="打率分布グラフ"><a href="#%E6%89%93%E7%8E%87%E5%88%86%E5%B8%83%E3%82%B0%E3%83%A9%E3%83%95">打率分布グラフ</a></h3> <p>リーグも長く運営されており、きれいな正規分布とはいかないまでもそれなりのデータが蓄積されています。</p> <p><a href="https://crieit.now.sh/upload_images/aa4dd599478ee4d40f9496e64b955de75c025692ec31f.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/aa4dd599478ee4d40f9496e64b955de75c025692ec31f.png?mw=700" alt="image" /></a></p> <h3 id="各選手能力チャート"><a href="#%E5%90%84%E9%81%B8%E6%89%8B%E8%83%BD%E5%8A%9B%E3%83%81%E3%83%A3%E3%83%BC%E3%83%88">各選手能力チャート</a></h3> <p>標準偏差を求める処理が若干重いのが難点ですが、選手の特徴を文章よりも雄弁に語ってくれます。</p> <p><a href="https://crieit.now.sh/upload_images/7dde7d39177c2fc8ac81e6e469fa63b55c0256b957aab.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/7dde7d39177c2fc8ac81e6e469fa63b55c0256b957aab.png?mw=700" alt="image" /></a></p> <h1 id="課題"><a href="#%E8%AA%B2%E9%A1%8C">課題</a></h1> <h2 id="APIのレスポンスが遅い"><a href="#API%E3%81%AE%E3%83%AC%E3%82%B9%E3%83%9D%E3%83%B3%E3%82%B9%E3%81%8C%E9%81%85%E3%81%84">APIのレスポンスが遅い</a></h2> <p>低スペックのサーバを使っているため、APIの応答が約1秒かかっています。これはサーバの載せ替えを考えています。</p> <h2 id="デザインセンスがない"><a href="#%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3%E3%82%BB%E3%83%B3%E3%82%B9%E3%81%8C%E3%81%AA%E3%81%84">デザインセンスがない</a></h2> <p>業務システムの経験が長かったのでUIのセンスが壊滅的です。<br /> UIは未だに試行錯誤中です。</p> <h2 id="テストコードを書いていなかった"><a href="#%E3%83%86%E3%82%B9%E3%83%88%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E6%9B%B8%E3%81%84%E3%81%A6%E3%81%84%E3%81%AA%E3%81%8B%E3%81%A3%E3%81%9F">テストコードを書いていなかった</a></h2> <p>基本人力テストです(笑)<br /> 最近ようやくJest使い始めたのでテストコード書こう...。</p> <h2 id="ページが多い"><a href="#%E3%83%9A%E3%83%BC%E3%82%B8%E3%81%8C%E5%A4%9A%E3%81%84">ページが多い</a></h2> <p>前述のUIの話になるのですが、ページ数が多いのでどういうメニューにして導線をどうするか…という点です。要検討。</p> <h1 id="まとめ"><a href="#%E3%81%BE%E3%81%A8%E3%82%81">まとめ</a></h1> <p>この記事をレビューしていただいた<a target="_blank" rel="nofollow noopener" href="https://twitter.com/nabettu">nabettu</a>さんに感謝を。<br /> 現行システムがEOLの技術を使っているので、なるべく早く移行したいとは思っていますが、ロジックが多い&最適化できていない上にソースの量が多くてなかなか開発が進まない状況です。<br /> ついつい現行システムに新しい機能を追加して移行のための開発を後回しにしてしまったり...。(エンジニアあるある?)<br /> 野球リーグの広報も兼任していて試合の動画作ったりもしているので、ただでさえ土日しか開発できないのに時間が足りなくて辛い。</p> <h1 id="おわりに"><a href="#%E3%81%8A%E3%82%8F%E3%82%8A%E3%81%AB">おわりに</a></h1> <p>明日は<a href="https://crieit.net/users/8398a7">839</a>さんの記事です!お楽しみに。</p> ckoshien tag:crieit.net,2005:PublicArticle/14558 2018-10-02T16:06:33+09:00 2018-10-23T16:45:29+09:00 https://crieit.net/posts/Kill-Bill 今自分が調査しているオープンソースソフトウェア「Kill Bill」について <p>仕事する気力がないので、少し自分の仕事内容を公言したらやる気が出るかもしれないと思ったので、やってみる。</p> <h1 id="Who am I"><a href="#Who+am+I">Who am I</a></h1> <ul> <li>札幌在住の派遣プログラマ <ul> <li>Python, JavaScript, PHP, Nim, Go, Elm, HTML/Pug, CSS/Stylus, etc...</li> <li>パソコンの使える創作活動は手当たり次第手を出している</li> <li>ろくな大人じゃないのにCoderDojoのメンターを堂々とやってる</li> </ul></li> <li>時給1000〜1200円をうろうろ <ul> <li>今回話す「Kill Bill」の調査に加えて、以下を並行して習得しろとのお達しがあった。 <ul> <li>コンテナ仮想環境技術を利用した自動並列分散オーケストレーションの実装(実務に耐えれるもの): 1ヶ月</li> <li>Go言語によるメタプログラミング(セキュリティなどの問題も考慮し、実務に応えれるもの): 2ヶ月</li> </ul></li> <li>私の残り出向先での契約期間: 2ヶ月</li> </ul></li> <li>Qiitaとかteratailとかにも出没している。</li> </ul> <h1 id="What's &quot;Kill Bill&quot; ?"><a href="#What%27s+%26quot%3BKill+Bill%26quot%3B+%3F">What's "Kill Bill" ?</a></h1> <p>布袋寅泰の「BATTLE WITHOUT HONOR OR HUMANITY」でお馴染み、クエンティン・タランティーノのあの映画……ではない。</p> <p>オープンソースで開発されている「購読型決済管理システム」という私の認識である。</p> <p><a target="_blank" rel="nofollow noopener" href="http://killbill.io/">Open-Source Subscription Billing & Payment Platform - Kill Bill</a><br /> <a target="_blank" rel="nofollow noopener" href="https://github.com/killbill">Kill Bill (GitHub)</a></p> <p>もちろん名前は映画の影響もあると思うが、「BillingシステムをKillする」という思いを込めたものだったとどこかに書いてあった気がする。</p> <h2 id="何がすごいの?"><a href="#%E4%BD%95%E3%81%8C%E3%81%99%E3%81%94%E3%81%84%E3%81%AE%EF%BC%9F">何がすごいの?</a></h2> <p>オープンソースで決済システムを作っているという時点で凄いだろ?!</p> <h3 id="ものすごく良く錬られた設計"><a href="#%E3%82%82%E3%81%AE%E3%81%99%E3%81%94%E3%81%8F%E8%89%AF%E3%81%8F%E9%8C%AC%E3%82%89%E3%82%8C%E3%81%9F%E8%A8%AD%E8%A8%88">ものすごく良く錬られた設計</a></h3> <p>設計、特にデザインパターンやイベント駆動型プログラミングをもう存じ上げている方はすぐに読めるかもしれないが、惜しみなく使っている。</p> <p>そしてテストコードもしっかりある。</p> <p>今のソフトウェア開発では当然だと思うが、その良さがはっきり分かっていないという人には良い教材だと思う。</p> <p>試しにKill Billのプラグインを作って見るだけでもかなり勉強になる。<br /> もし余裕があればUMLの様な図を書きながら、ソースを解読していっても良いだろう。</p> <h3 id="プラグインを作れる"><a href="#%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%E3%82%92%E4%BD%9C%E3%82%8C%E3%82%8B">プラグインを作れる</a></h3> <p>プラグインを作れる。これがどうありがたいかというと、提供されていない決済代行会社向けに自分たちでプラグインを開発できるのもメリット。</p> <p>また、Kill Billでかゆいところに手が届かない処理も、ある程度プラグインを作ることによってサポートできるかもしれない。</p> <p><a target="_blank" rel="nofollow noopener" href="http://docs.killbill.io/0.20/plugin_development.html">Plugin development</a></p> <h1 id="誰が使うの?"><a href="#%E8%AA%B0%E3%81%8C%E4%BD%BF%E3%81%86%E3%81%AE%EF%BC%9F">誰が使うの?</a></h1> <p>お金が絡むシステムを設計から実装までするのって、怖くないですか?<br /> 私は怖いし、うちのチームリーダーも怖いので、これの採用しようかという話になってる。</p> <p>「ソフトの信頼性はどうなのよ? 誰かが趣味で作ってるオープンソースならもっと怖いでしょ……」<br /> そのとおりである。<br /> しかし一応「GROUPON」や「Google Cloud」も Trusted by に入ってるので、大丈夫じゃないかなあ……。</p> <hr /> <p>とりあえず、よく出来てると思うオープンソースソフトウェアなので、紹介だけする。</p> まんじゅ(´ん`)