幸いにも計算部分は Perl の実装を参考にさせていただくことができたので、Java 風のアレンジを加えてみました。
まずタグのインタフェースを定義します。Comparable を継承しているのはソートに使用するためです。
public interface Tag extends Comparable {
/** タグそのものを返します */
String getValue();
/** タグの使用回数など指標になる数値を返します */
int getScore();
/** タグクラウドの文字の大きさを返します */
int getCloudLevel();
/** タグクラウドの文字の大きさを設定します */
void setCloudLevel(int cloudLevel);
}
次にインタフェース Tag の実装クラスです。
import java.io.Serializable;
public class ExampleTag implements Tag, Serializable {
private String value;
private int score;
private int cloudLevel;
public ExampleTag(String value, int score) {
this.value = value;
this.score = score;
}
public String getValue() {
return value;
}
public int getScore() {
return score;
}
public int getCloudLevel() {
return cloudLevel;
}
public void setCloudLevel(int cloudLevel) {
this.cloudLevel = cloudLevel;
}
public int compareTo(Object o) {
Tag tag = (Tag) o;
return (getScore() - tag.getScore());
}
public String toString() {
return "ExampleTag[value=" + value + ",score=" + score
+ ",cloudLevel=" + cloudLevel + "]";
}
}
最後にタグクラウドの実装です。main にテスト用のコードを書いておきます。TagCloud クラスを作成して Tag オブジェクトを addTag していって最後に getTags で取り出して使います。順序を維持するために java.util.LinkedHashSet を使っています。
import java.util.Iterator;
import java.util.Set;
public class TagCloud {
/** フォントの大きさの段階を指定 */
private final int maxLevel;
/** タグを順序どおりに格納する Set */
private final Set<Tag> tags;
/** 計算済みフラグ */
private boolean clouded;
/** 最小 */
private int minScore = Integer.MAX_VALUE;
/** 最大 */
private int maxScore = Integer.MIN_VALUE;
/** コンストラクタ */
public TagCloud(int maxLevel) {
this(maxLevel, 16);
}
/** コンストラクタ */
public TagCloud(int maxLevel, int capacity) {
if (maxLevel < 1) {
throw new IllegalArgumentException("invalid maxLevel " + maxLevel);
}
this.maxLevel = maxLevel;
this.tags = new java.util.LinkedHashSet<Tag>(capacity);
}
/** タグを追加します */
public void addTag(Tag tag) {
if (tag != null) {
tags.add(tag);
int score = tag.getScore();
this.minScore = Math.min(minScore, score);
this.maxScore = Math.max(maxScore, score);
this.clouded = false;
}
}
/** タグの Set を返します */
public Set getTags() {
cloud();
return tags;
}
/** タグクラウドのための計算をします */
private void cloud() {
final int size = tags.size();
if (clouded || size == 0) {
return;
}
if (size == 1) {
Tag tag = tags.iterator().next();
tag.setCloudLevel(1);
} else {
double min = (minScore == 0) ? 0.0: Math.log(minScore);
double max = (maxScore == 0) ? 0.0: Math.log(maxScore);
double f;
if (min == max) {
min -= maxLevel;
f = 1.0;
} else {
f = maxLevel / (max - min);
}
if (size < maxLevel) {
f *= ((double) size / maxLevel);
}
for (Tag tag: tags) {
int score = tag.getScore();
if (score != 0) {
tag.setCloudLevel((int) ((Math.log(score) - min) * f));
}
}
}
this.clouded = true;
}
// NOTE: for test
public static void main(String[] args) {
TagCloud tagCloud = new TagCloud(4);
tagCloud.addTag(new ExampleTag("abcd", 3));
tagCloud.addTag(new ExampleTag("efg", 1));
tagCloud.addTag(new ExampleTag("hijk", 2));
tagCloud.addTag(new ExampleTag("lmn", 10));
tagCloud.addTag(new ExampleTag("opqrstu", 7));
tagCloud.addTag(new ExampleTag("vwxyz", 5));
System.out.println(tagCloud.getTags());
}
}
上記 3つのソースを同じディレクトリにおいて javac *.java でコンパイル、java TagCloud でテスト実行すると以下のような結果が出力されます。
[ExampleTag[value=abcd,score=3,cloudLevel=1],
ExampleTag[value=efg,score=1,cloudLevel=0],
ExampleTag[value=hijk,score=2,cloudLevel=1],
ExampleTag[value=lmn,score=10,cloudLevel=3],
ExampleTag[value=opqrstu,score=7,cloudLevel=3],
ExampleTag[value=vwxyz,score=5,cloudLevel=2]]
このクラスを利用して JSP などで HTML 出力すれば
<style type="text/css">
.tagcloud {width:180px;}
.tagcloud span {padding:5px; font-weight:bold; color:steelblue;}
.tagcloud .tag0 {font-size:10px;}
.tagcloud .tag1 {font-size:16px;}
.tagcloud .tag2 {font-size:22px;}
.tagcloud .tag3 {font-size:28px;}
</style>
<div class="tagcloud">
<span class="tag1">abcd</span>
<span class="tag0">efg</span>
<span class="tag1">hijk</span>
<span class="tag3">lmn</span>
<span class="tag3">opqrstu</span>
<span class="tag2">vwxyz</span>
</div>
こんな感じのタグクラウドになります。
0 件のコメント:
コメントを投稿