題名:Java Diary-4章

五郎の入り口に戻る

日付:1998/2/5

目次に戻る


DocBrower

さてなんとなくではあるが、とりあえず「練習」のリンク集はそれなりに完成した。さてここでようやく本題である「Javaで作った文書閲覧ソフト」のほうにとりかかろうかと思うのだが。。。

リンク集を作ってJavaでなにができるかはなんとなくわかった。そしてやっかいなHTMLを読み込んで、解析する方法も(これは結構自信がないが)とりあえずできた。閲覧の対象になる文章のほうも結構完成してきて、何をどのように閲覧すればよいかなんとなくわかってきた。従ってなんとなく概念設計はできたようなものである。

次にいくつかキーとなる部分をざっとあたってみる必要がある。するといくつか思いうかぶ要素がある。

 

  • アプレットにするか、それともアプリケーションにするか?

アプレットにした方が実行は楽だろう。しかし閲覧の度に読み込むのでは遅くてしょうがないかもしれない(実際どの程度のプログラムになるか検討がつかない)おまけにものの本によれば、アプレットでは、新しいウィンドウを開く際に、ブラウザ側の制限が多きいかもしれない。一つのウィンドウだけで自由な閲覧を行うのは難しいかもしれない。

アプリケーションにすればそれらの問題は解決する。しかし、大抵の環境でJavaアプリケーションを実行する環境がそんなに整っているのだろうか?それを考えれば当面アプレットで考えるべきだろう。

  • 行間指定ができ、リンクをもたせられるるテキストエリアの作成

これが難関である。質問一覧にも書いたことであるが、なんといってもつまった文字を読むのは疲れる。なぜかいまのところHTMLで行間を指定する方法はないようだ。となるとなんとかそういうテキストエリアを作成する必要がある。

またこのテキストからはリンクをもたせることができなくてはならない。そうでないとリンクのスパゲッティとなっている私の文書を読むことは難しいからだ。文章が自由に行間を指定して閲覧でき、かつリンクが存在している部分にカーソルを持っていくか、あるいはクリックするとリンク先が参照できる、そういったテキストエリアがほしい。

上記の問題が解決すれば、先は長いがなんとか形になりそうな気がする。しかし特に2番目の問題は解決がややこしそうだ。しばらくインターネットをあさってみたが、どうも該当するソフトウェアは見つからない。しかしいちから自分で作るとなると結構なお仕事となりそうだ。

もうちょっと腰を据えて探してみるか。。

 さてそれからの進行状況は。。。まことにのろのろしている。ある日私の頭に啓示のような考えが浮かんだ。TextAreaはなにが難しいといって、文字の入力ができることが難しいに違いない。今回私が作ろうとしているのは閲覧ソフトだから、難しいところを避けて通れる。だからなんとかなるかもしれない。

それからCanvasにdrawStringで文字を表示する、というシンプルな構成で進みだいした。そしてなんとか表示はフォントのサイズと行間を指定して行えるようになった。しかし(予想したこととはいいながら)いろいろとややこしい文章をきちんと行をおりかえし、かつページを区切って表示するのはとても面倒な仕事であることがわかった。

しかしなあ。。表示だけでこんなに難しいんだから、編集とかを含めたらどれほど大変なのだろう。。。と(これまたあたりまえのことだが)エディタを作る人に尊敬の念をささげた。プロフェッショナルならともかく、なかにはフリーウェアやシェアウェアでエディタを作る人まで結構たくさんこの世の中には存在するのである。

また、またもや環境による結果の違いに私は悩まされることになった。

表示する行がちゃんとおさまるか?という判定にFontMetrics.fontWidthという関数を使っている。ところが1バイト文字と2バイト文字が混ざっていると「時々」fontWidthはまともな値を返さないのである。これが「時々」というところがみそで、まともな値を返すこともあるからややこしい。

上記のバグはMRJ2.0の場合である。例によって「これはPreview Releaseだからだよーん」と思ってinternet ExplorerのMicrosoftのJavaの環境でトライしてみた。今度は見事に行の折り返しが表示される。しかし今度はなぜか行末に妙なキャラクターが表示される、という症状に悩まされることになる。不思議なことにこのキャラクターがどこからきているのかわからない。たとえば"teikade"という文字列を、あれこれ操作したあとでdrawStringで表示しようとすると、

teikade>>(>>のところは変なキャラクター)が表示される。ところがそのStringと"teikade"をequals()で比較すると「全く同じだ」という答えを返してくるのである。

そこで私はまたもや「こそくな」解決策をとることになった。しょうがないから例のキャラクターが表示されるいちをCanvasと同じ色で塗りつぶすことにしたのである。これに関連していくつかのバグも発見できたが。。。これでは対応できないケースもあることがわかった。でもまあいいや。私はアマチュアだー。と開き直ることにした。

ところが問題はまだあった。

Internet Explorerでページをめくった状態で、行間やフォントのサイズを変更するとなんと妙な無限ループにはいることが判明したのである。これはもっと大きな問題だ。世の中にはMicrosoftのJava環境を使っている人のほうがずっと多いだろうから、もしこれを公開して、かつ使ってみるという奇特な人ができた場合には、私はひどい文句を被ることになる。

 

とかなんとかやっていたら結構日が経ってしまった。おまけにその間にホームページのコンテンツの方の更新は止まったままである。

 

さて。。。よくよく探求してみたら、無限ループの原因は非常に奇妙なところにあった。

if(indexToDisplay == -1){

 (何かの処理:indexToDisplayはさわってません。。。)

else{

 (別の処理:indexToDisplayはさわってません。。。)

  if(indexToDisplay == -1){

   System.out.println("this doesn't make sense however break;");

   break;

  }

理屈から言えば、"this doesn't make sense however break;"などという妙なメッセージがconsoleに表示されることはありえない。ところが現実問題としてこのメッセージが表示されるのである。

ここでプログラムの経験の浅い人であれば「コンパイラのバグだ」と声高らかに叫ぶであろう。

あるいはCもしくはC++のプログラマである程度経験をつんだ人であれば、「それはきっと配列かポインタの処理を間違えて、さわっていないつもりの領域を書き換えちゃったんだよ」と言うだろう。たとえば

int x[10];

int z;

などという宣言があり、x[10]あたりを書き換えれば、zがとんでもない値になっても文句は言えない。プログラム中でZを一回もさわらないにもかかわらずである。(実際これはとても問題があり、見つけにくいバグだ)

ところがぎっちょん。たてまえから言えばこれはJavaではおこらないはずなのだ。(それがJavaの売りの一つなのだから)ポインタは存在しないし、私はこの部分では配列すら使用していない。

 

しかし現実は認めなくてはならない。実際上記のプログラムで妙なメッセージは表示されるのだ。

 

このバグをとろとうしている最中にもう一つ気づいたことがある。MRJ2.0とMicrosoftのjavaでは、どうも各コンポーネントにpaint()を送っているタイミングが違うようだ、ということである。MRJ2.0でうまく表示されるようにすると、Microsoftでやたら画面が書き換えられ、ちらつく。Microfostでのちらつきを押さえると、今度はMRJでちゃんと表示がなされない。なんだこれは?と思ってはみたもののこれまた事実は尊重しなくてはならない。。

そうだこうだといいながらもなんとか文章を表示し、ページをめくる、という当初の目的は達成されつつあった。そこから一歩進もうとした瞬間、私はデータベースの構造をもっと慎重に考える必要があることに気がついた。いままでは「とにかく表示できればいいや」とやってきたのだが、まじめに考えないとデータが崩壊してしまいそうだ。現に今のプログラムも使っていないインスタンス変数がやたらたくさんできてしまっている。

そこで私は紙の上に戻り、データ構造を考えることにした。こういった方法の一つの欠点は、紙に書いている途中から「こんなものを書いているくらいだったら、プログラムを書いたほうが早いわい」と叫んで、コーディングにかかってしまい、、、同じ問題を繰り返す危険性があることである。しかし今回、私はその誘惑からかろうじて逃げおおせた。

 

さて2月も終わりに近づいたある日、とうとうなんとか文章表示部分がちゃんと動くようになった。データ構造をちゃんと考え直したおかげで、なんとかまともに動いているように見える。ゆっくりとおちついて考えたら、Internet Explorer 4.0で妙な文字が表示される原因も分かった。これは全く日頃からの主張に反するばかげたデバッグをやったがために見つからないバグだった。

プログラム中では、java.awt.canvasに対してdrawStringを呼んでいるが、「理論的」にはここで引数に「\n」渡らない「はず」である。従って私は妙な文字がまぎれこんでいるのではないか、などと血眼になって原因を探していたのである。

ところが原因はまさにその「はず」が「はず」でない、というあまりにもありふれたところにあった。drawStringの引数をチェックしたら、紛れもなく"\n"がわたっていたのである。それさえわかれば直すのは簡単であった。。。。不具合の時は、まず一番直接的なところから「事実」を確かめていくべきだ、とは何度も何度も痛い目をして学んだ経験のはずだ。なのにまたやってしまった。直接的な原因は「理論的にあり得ない」と目をつぶってしまい、あさってのほうばかり探求していたのである。

一時はソフトウェアのほうで職を探そうか、と思った人間がやっていることがこれである。なんといことだ。。。所詮私は出来の悪いアマチュアか。。。

などと妙な落ち込みをしている場合ではない。とりあえず(まだ問題はいくらでもあるが)文章の表示(含む注記の表示、一連のファイルの表示)ができてしまったのである。さてようやく本来の目的である「Javaを使ったナビゲーション」の実現の時だ!そこで私はまたもや自分の知恵の浅はかさを思い知ることになる。何を作ったらいいか?という問題に突き当たってそこからしばらく進めなくなったのである。。。。

 

おまけに気候はようやく春になろうとしていた。春になれば気候がよくなり、、、はれた平日に部屋の中でコンピュータに向かってプログラムを書いていることがいかにばかばかしいか。。おまけに私は職も探していた。

世の中は春なのだな。。などと明るくなった窓の外を見て感慨に耽っている場合ではない。職が見つかり、このプログラムが半分できたまま放置されるような事態になってはならない。深遠な理想は理想としてがりがりと進めなくてはならない。とりあえずきちんと動くようにしなければ、このプログラムは一生日の目を見ないだろう。

幸いなことになんとなく私が気にいった画面があった。大抵のJDKについてくる、Sun特製のGraphLayoutというアプレットである。実のところ何をやっているのかわからないが、リンクされたノードがぐにょぐにょと動きながらいつしか平衡状態に落ち着いていく、というプログラムだ。

なぜこの画面が私の気に入るところとなったか?早い話、今回作った駄文の山はリンクのスパゲッティのような代物である。文章と文章は共通のキーワードや参考文献を介してリンクされていたりする。ってえことは、そういったリンクをつりつりとたどっていくような形で構成が表示できれば、この文章から、このキーワード、そしてほかの文章、という感じに文章を読み渡っていくことができる(のではないか)と考えたのである。そしてこのGraphLayoutの画面はそういった構造を表示するのにぴったりの機能をもっていた。

もう一案として、link集のところで用いたのと同じような構造の画面、すなわちJavaの標準コンポーネントを使って(リストとかテキストとか。。)表示する画面も考えた。しかしどう考えてもおもしろくない。こんな画面だったら仕事でも作れる。どうせ素人が暇つぶしにやっていることだから、おもしろくしようじゃねぇの。

というわけで、GraphLayoutを基本にして(というかコピーを下敷きにして)しょこしょこプログラムを作り始めた。あたりまえと言えばあたりまえなのだが、文章のタイトルを表示するところまでは実に簡単にすすんだ。もっとも本来であれば、これまで避けてきたThreadに関する勉強をこのとき私はするべきであった。グラフがグニョグニョ動くアニメーションはthreadを用いて実装されていたからである。よくよくプログラムをみてみればSynchronizedとかいう見慣れないキーワードがでてくる。これは何だ?と思ったが「とりあえず前進」というこれまた何度も痛い目にあったが、こりない方針に従って適当に作り始めた。

さて2-3日で文章のタイトル、参考文献のタイトル、キーワード(トピックスと呼んでるが)を表示するところまではできた。問題は「並べ替え」であった。

なぜ並べ替えが必要か?たとえばあるタイトルを選んだ時に、それに関連する項目がそのタイトルの近くに集まるようにしたかったからである。

基本的なアルゴリズムは「隣をみる。自分より重いやつが自分より上にいたら、そいつを押しのけて上に行く」とこんだけである。そこで適当にインプリメントしてみると、これがまた妙におもしろいアニメーションを表示することがわかった。それとともにいろいろ欠けている機能にも気がついた。最終的にノードとノードの距離を一定に保ち、かつ画面内におさめなくてはならず。。。と場当たり的にアルゴリズムを追加していった結果として、私は二つのことに気がついた。

1つは全くアルゴリズムが何をやっているのか自分でも理解ができなくなったことである。もうひとつはソートの最中のアニメーションが妙におもしろくなったことである。理解できないアルゴリズムがうまく動く可能性はとても少ない。。。だからソートも不完全なものだった。ノードは重なってしまうし、やたらはずれた方向に表示がなされたりする。しかし不思議なことにテストしたすべてのケースで最終的にはノードの動きは収束し、かつそれまでのアニメーションはとても意図してできるとは思えないほどおもしろかった。

というわけで私はしばしばその画面に見とれていた。。。。遠い昔の日々、PC-8001で機械語のプログラムを必死に雑誌から打ち込んでいた頃、時々「こんなおもしろい画面ができるか」と思えるような「暴走」の仕方をすることがあった。。しばし私はその遠い日の思い出にひたっていた。。。しかし冷静に帰って考えてみると、自分で作った画面に見とれている30男(しかも失業中)というのはあまりほめられた図ではない。おまけに外は明るい日差しにあふれた春の日だ。。。

はっと我に返った私はもうちょっとまともなアルゴリズムを考えることにした。幸いなことにある日私は来るまで遠くにおでかけであった。この日はいやでもコンピュータにさわることはできないし、ハンドルを握っている間考える時間はたっぷりある。

私がたくさんもっている信念のうちのひとつ"Keep it Simple, Stupid"に従って考えてみると、、なんだ簡単にできるではないか。多少のことには目をつぶろう。少なくともこのアルゴリズムで行けば自分が何をやっているか理解できなくなることはなさそうだ。。

翌日さっそく私はそのアルゴリズムをインプリメントした。例によっていろいろなバグをぷちぷちつぶしていくと。。。おっと見事に動くではないか。ちょと気が抜けるほどあっさり動いた。ところが残念なことにアニメーションは全くおもしろくなくなってしまった。アルゴリズムが単純明快だからあたりまえと言えばあたりまえなのだが。。

 

よし。これであとはナビゲーション画面とテキスト表示画面の結合だ。(いままでは面倒をさけるため別のアプレットとして作成して試験をしていたのである。)ここでまたもう一つのハードルにチャレンジする必要に迫られた。

普通のアプリケーションであれば、ナビゲーション画面は別のウィンドウに表示して、いつでも参照できるようにしたいところである。しかしアプレットから別のウィンドウを開こうとするといろいろ面倒だ。Frameというクラスを使えばできるのだが、ブラウザからFrameを使用して新しいウィンドウを表示させると下の方に「Warning, Applet Window」とかいう表示が自動的についてしまう(Security上の配慮なのだそうだ)ただでさえ狭い画面がますます狭くなってしまうではないか。。おまけにいままでFrameは使ったことがないのである。

えーい。ごちゃごちゃ言っている場合ではない。さっさと前進だー。ととりあえずFrameを使ったウィンドウにいろいろ表示するプログラムを書いてみたのだが。。。確かに新しいウィンドウは表示される(例の「警告文書」付きだが)。ところがなぜかその上に配置したはずのコンポーネントは表示されないのである。

私はその「小さな」のっぺらぼうのウィンドウを眺めてしばらく凍り付いていた。

そしてこれから新しい「Frame」にチャレンジする際に突き当たる出あろう困難に思いをはせていた。正直言ってここまでくるのに重大なバグをいくつも見逃してきている。。いままでなんとか動いている、と思えるところだっって、まだバグだらけで人様に公開できるようになるまでには大分かかるだろう。それを思うと。。。

頭をふって、Frameを頭のなかから追い払った。そして「Panelの上に表示するコンポーネントのvisibleを切り替える」というおもしろくはないが、間違いなく動く方法に転向した。

 

二つの画面の結合は思ったよりスムーズにできた。文章を読み込んで、必要に応じてアクセスできるようにする部分をTextDBとしてデータベースクラスとして分離しておいたのが功を奏した。(ちなみにこれは以前仕事でプログラムを書いていたときにさんざんつかった手法である)

そしてその日の夜には、とうとう最初に描いたような機能を持ったアプレットが動くようになった。。。もちろんある特定の場合にのみである。例によって誤表示やらフリーズが連発する。しかしたとえばデモには使用できるようなものができた。。。

さてこれからがまた長い道のりだ。。今まで目をつむってきた課題リストは山のようになっている。「小さな」課題をつぶし、デバッグを延々と続けなくてはならない。。(1998/3/8)

次の章へ


注釈

不具合の時は、まず一番直接的なところから「事実」を確かめていくべきだ後悔は有史以来先にたった試しはない。3度とは言わないが、なんとか4回くらい同じことをしたらなんとか学ぶことができるようにならないものだろうか。。本文に戻る

 

基本的なアルゴリズム:たぶんこのアルゴリズムはもっと洗練されて世の中で名前がついているのだろう本文に戻る

 

機械語のプログラムを必死に雑誌から打ち込んでいた:いまではとても信じられないことだが、当時はこれができたのである。早いゲームは大抵機械語でかかれていて、しかも大きさは信じられないほど小さかった。もっともわけのわからない16進数を延々と打ち込むのは若いからできたことかもしれないが。本文に戻る

 

"Keep it Simple, Stupid":KISSと略される。とにかく物事を単純に、馬鹿みたいにするようにつとめるという意味。非常にあちこちでお目にかかる言葉だが語源がどこかわからない。本文に戻る