OSDN Git Service

指定したキーワードに関連するハッシュタグを取得する機能の基礎部分が完成.GUI部分はまだ作成していない.
authorspark_xp <spark_xp@d8c9ecd3-d47d-4367-8645-de82c00e513f>
Sun, 19 Sep 2010 10:12:58 +0000 (10:12 +0000)
committerspark_xp <spark_xp@d8c9ecd3-d47d-4367-8645-de82c00e513f>
Sun, 19 Sep 2010 10:12:58 +0000 (10:12 +0000)
git-svn-id: http://svn.sourceforge.jp/svnroot/nt-manager/NishioTweetManager/trunk@81 d8c9ecd3-d47d-4367-8645-de82c00e513f

src/twitter/hashtag/HashtagSearcher.java [new file with mode: 0644]
src/twitter/util/MultiSortedMap.java [new file with mode: 0644]
test/twitter/hashtag/HashtagSearcherTest.java [new file with mode: 0644]

diff --git a/src/twitter/hashtag/HashtagSearcher.java b/src/twitter/hashtag/HashtagSearcher.java
new file mode 100644 (file)
index 0000000..396b6d9
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * 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;
+    }
+}
diff --git a/src/twitter/util/MultiSortedMap.java b/src/twitter/util/MultiSortedMap.java
new file mode 100644 (file)
index 0000000..fc85610
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * 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;
+       }
+}
+
diff --git a/test/twitter/hashtag/HashtagSearcherTest.java b/test/twitter/hashtag/HashtagSearcherTest.java
new file mode 100644 (file)
index 0000000..7c0f460
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * 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