--- /dev/null
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package twitter.hashtag;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import twitter.manage.TweetManager;
+import twitter.util.MultiSortedMap;
+import twitter4j.Status;
+
+/**
+ * 指定したキーワードに関係のあるハッシュタグを検索する
+ * @author nishio
+ */
+public class HashtagSearcher {
+
+ //twitter管理クラス
+ private TweetManager tweetManager;
+ //ハッシュタグのパターン
+ private static final String HASHTAG_PATTERN = "#[0-9A-Za-z_]+";
+ //検索情報数
+ private static final int MAX_SEARCH_NUM = 100;
+
+ /**
+ *
+ * @param tweetManager
+ */
+ public HashtagSearcher(TweetManager tweetManager) {
+ this.tweetManager = tweetManager;
+ }
+
+ /**
+ * 検索したワードに関するハッシュタグを出現頻度準に取得
+ * @param searchWord
+ * @return 見つからなかったらnull, 見つかったらkey=出現回数, value = 出現ワードが出現頻度降順に並べられて出力される
+ */
+ public MultiSortedMap<Integer, String> getDescendantHashtagCount(String searchWord) {
+ MultiSortedMap<Integer, String> counter = null;
+ Map<String, Integer> searchWordCounter = calcSearchedHashtagCount(searchWord);
+ if( searchWordCounter != null ) {
+ //検索ワード見つかった時
+ counter = new MultiSortedMap<Integer, String>(new Comparator<Integer>(){
+
+ //keyを降順にソート
+ @Override
+ public int compare(Integer t, Integer t1) {
+ return t1.compareTo(t);
+ }
+
+ });
+
+ Set<String> keyset = searchWordCounter.keySet();
+ for( String key : keyset ) {
+ //keyとvalueを逆転させる
+ Integer val = searchWordCounter.get(key);
+ counter.add(val, key);
+ }
+ }
+ return counter;
+ }
+
+ /**
+ * 指定したワードに関連するハッシュタグをツイッターから取得
+ * @return 検索結果が見つからない場合はnullを返す
+ * 見つかった場合は,key=検索ワード, val = 出現数
+ */
+ public Map<String, Integer> calcSearchedHashtagCount(String searchWord) {
+ //指定したワードをツイッターから検索
+ List<Status> searchResult = this.tweetManager.getSearchResult(MAX_SEARCH_NUM, searchWord);
+ if( searchResult == null || searchResult.size() == 0 ) {
+ return null;
+ }
+ //ハッシュタグ出現回数
+ Map<String, Integer> counter = new HashMap<String, Integer>();
+
+ for(Status s : searchResult ) {
+ //検索してきたワードを含むテキスト
+ String message = s.getText();
+ if( message != null ) {
+ //検索してきた1ツイートのハッシュタグ出現回数を取得
+ Map<String, Integer> oneTweet = getHashtagCount(message);
+ //出現回数
+ Set<String> keyset = oneTweet.keySet();
+
+ for(String key : keyset) {
+ //いままでその単語が出現していたかどうか
+ Integer count = counter.get(key);
+ if( count == null ) {
+ count = new Integer(0);
+ }
+ //いままで出現していた単語数に新しく出現した単語数を足す
+ count = count + oneTweet.get(key);
+ counter.put(key, count);
+ }
+ }
+ }
+
+ if( counter.size() == 0 ) {
+ return null;
+ }
+ return counter;
+ }
+
+ /**
+ * 指定したメッセージに含まれるハッシュタグの出現回数を返す
+ * @param message
+ * @return key = キーワード, value = 出現回数
+ */
+ public Map<String, Integer> getHashtagCount(String message) {
+ // #で始まる情報
+ Pattern userPtn = Pattern.compile(HASHTAG_PATTERN);
+ Matcher matcher = userPtn.matcher(message);
+
+ // #で始まる情報一覧を抜き出す
+ Map<String, Integer> counter = new HashMap<String, Integer>();
+
+ while (matcher.find()) {
+ //ハッシュタグを探しカウントしていく
+ String str = matcher.group(0);
+ Integer val = counter.get(str);
+ if( val == null ) {
+ val = new Integer(0);
+ }
+ val = val + 1;
+ counter.put(str, val);
+ }
+ return counter;
+ }
+}
--- /dev/null
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package twitter.util;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+/**
+ * sort済みマルチマップ
+ * @author nishio
+ * @param <T>
+ * @param <V>
+ */
+public class MultiSortedMap<T, V> {
+
+ private SortedMap<T,SortedSet<V>> mmap;
+
+ public MultiSortedMap() {
+ mmap = Collections.synchronizedSortedMap( new TreeMap<T,SortedSet<V>>() );
+ }
+
+ public MultiSortedMap(Comparator<? super T> cmprtr) {
+ mmap = Collections.synchronizedSortedMap( new TreeMap<T,SortedSet<V>>(cmprtr) );
+ }
+
+ /**
+ *
+ * @param key
+ * @param value
+ */
+ public void add(T key, V value){
+ SortedSet<V> mapValue = null;
+
+ if(mmap.containsKey(key)) {
+ mapValue = mmap.get(key);
+ } else {
+ mapValue = Collections.synchronizedSortedSet( new TreeSet<V>() );
+ }
+
+ mapValue.add(value);
+ mmap.put(key, mapValue);
+ }
+
+ /**
+ *
+ * @param key
+ * @return
+ */
+ public SortedSet<V> get(T key){
+ if( mmap == null ) {
+ return null;
+ }
+ SortedSet<V> s = new TreeSet<V>();
+ s = mmap.get(key);
+
+ return s;
+ }
+
+ /**
+ *
+ * @return
+ */
+ public Set<T> getKeys() {
+ if( mmap == null ) {
+ return null;
+ }
+ return mmap.keySet();
+ }
+
+ /**
+ *
+ * @return
+ */
+ public Collection<SortedSet<V>> getValues() {
+ if( mmap == null ) {
+ return null;
+ }
+ return mmap.values();
+ }
+
+ /**
+ * 登録されているキーの数を返す
+ * @return
+ */
+ public int size() {
+ return mmap.size();
+ }
+
+ /**
+ *
+ * @param key
+ * @param value
+ * @return
+ */
+ public boolean contains(T key, V value) {
+ Set<V> t = get( key );
+ if( t != null ) {
+ return t.contains(value);
+ }
+ return false;
+ }
+
+ /**
+ *
+ * @param key
+ * @return
+ */
+ public boolean containsKey(T key) {
+ return mmap.containsKey(key);
+ }
+
+ /**
+ *
+ * @param key
+ */
+ public SortedSet<V> remove(T key) {
+ return mmap.remove(key);
+ }
+
+ /**
+ *
+ * @param key
+ * @param value
+ * @return
+ */
+ public boolean removeValue(T key, V value) {
+ Set<V> t = get( key );
+ if( t != null ) {
+ return t.remove(value);
+ }
+ return false;
+ }
+}
+
--- /dev/null
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package twitter.hashtag;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import twitter.manage.TweetManager;
+import static org.junit.Assert.*;
+import twitter.util.MultiSortedMap;
+
+/**
+ *
+ * @author nishio
+ */
+public class HashtagSearcherTest {
+
+ private TweetManager manager = new TweetManager();
+ private HashtagSearcher searcher;
+
+ public HashtagSearcherTest() {
+ try {
+ manager.loginTwitter();
+ } catch (FileNotFoundException ex) {
+ Logger.getLogger(HashtagSearcherTest.class.getName()).log(Level.SEVERE, null, ex);
+ } catch (IOException ex) {
+ Logger.getLogger(HashtagSearcherTest.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ searcher = new HashtagSearcher(manager);
+ }
+
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ }
+
+ @AfterClass
+ public static void tearDownClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() {
+ }
+
+ @After
+ public void tearDown() {
+ }
+
+ /**
+ * Test of getDescendantHashtagCount method, of class HashtagSearcher.
+ */
+ @Test
+ public void testGetDescendantHashtagCount() {
+ System.out.println("getDescendantHashtagCount");
+ String searchWord = "シャナ";
+ HashtagSearcher instance = searcher;
+
+ //TOO BAD TEST
+ MultiSortedMap<Integer, String> result = instance.getDescendantHashtagCount(searchWord);
+ for(Integer key : result.getKeys()) {
+ System.out.println("KEY:" + key + " value:" + result.get(key));
+ }
+ }
+
+ /**
+ * Test of calcSearchedHashtagCount method, of class HashtagSearcher.
+ */
+ @Test
+ public void testCalcSearchedHashtagCount() {
+ System.out.println("calcSearchedHashtagCount");
+ String searchWord = "#FF15";
+ HashtagSearcher instance = searcher;
+
+ //TOO BAD TEST
+ Map result = instance.calcSearchedHashtagCount(searchWord);
+ assertEquals(14, result.get("#FF15"));
+ }
+
+ /**
+ * Test of getHashtagCount method, of class HashtagSearcher.
+ */
+ @Test
+ public void testGetHashtagCount() {
+ System.out.println("getHashtagCount");
+ String message = "これは#hashタグのテストです";
+ HashtagSearcher instance = searcher;
+
+ Map result = instance.getHashtagCount(message);
+ assertEquals(1, result.get("#hash") );
+
+ message = "これは#hashタグのテストです#hash,#fj";
+ result = instance.getHashtagCount(message);
+ assertEquals(2, result.get("#hash"));
+ assertEquals(1, result.get("#fj"));
+ assertEquals(null, result.get("#test"));
+
+ }
+
+}
\ No newline at end of file