ローカル用ブックマーク
http://d.hatena.ne.jp/hyuki/20071220#bookmark
乗り遅れすぎですが、自分でもこういうの欲しかったので、作ってみました。とりあえず試作品。指定したURLのファイルしか取ってきません。文字コードによっては題名がちゃんと読めません。
簡易Webサーバを立てる方式にしてます。Javaで書きました。非常に書きづらいと分かっていたものの、自分で手っ取り早くサーバを書けるのがJavaしか無かったので。(Rubyだと、WEBrickというのを使えばできそうですね。)
起動は
$ java Bukma 80
で、ポート番号は可変になってます。※セキュリティ注意してください。
javascript:window.location='http://localhost:80/edit'
を設定。ここもポートを上のに合わせて。
ブックマークレットをたたくと、画像の上の画面が出ます。入力して送信すると、下の一覧が出ます。
リストはローカルのURLで読み直して使うようにしています。
import java.io.*; import java.net.*; import java.text.*; import java.util.*; public final class Bukma { private static final File FILE = new File("bookmarklist.html"); private static final String CHARSET = "Shift_JIS"; private static void outputForm(PrintWriter out, String url) throws IOException { out.print("<html><body>input comment."); out.print("<form method=\"get\" action=\"/register\">"); out.printf("<input type=\"text\" name=\"title\" value=\"%s\" size=\"60\">", getTitle(url)); out.print("<br><input type=\"text\" name=\"comment\" size=\"60\">"); out.printf("<input type=\"hidden\" name=\"url\" value=\"%s\">", url); out.print("<input type=\"submit\" value=\" REGISTER \">"); out.print("</form></body></html>"); } private static String getTitle(String url) throws IOException { HttpURLConnection conn = (HttpURLConnection)new URL(url).openConnection(); String contentType = conn.getContentType(); String enc; if (contentType.startsWith("text/html;charset=")) { enc = contentType.substring(18); } else { enc = CHARSET; } try { BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), enc)); // あとでHTMLParserに書き換え StringBuffer buffer = new StringBuffer(); for (String line; (line = reader.readLine()) != null;) { buffer.append(line); int index = line.toLowerCase().lastIndexOf("</title>"); if (index >= 0) { String s = buffer.toString().toLowerCase(); int index1 = s.indexOf("<title>") + 7; int index2 = s.indexOf("</title>"); return buffer.substring(index1, index2); } } return "(unknown title)"; } catch (UnsupportedOperationException ex) { ex.printStackTrace(); throw new IOException(ex.toString()); } } private static Properties getRemoteFiles(String url) throws IOException { Date now = new Date(); String id = new SimpleDateFormat("yyyyMMdd-HHmmss-SSS").format(now); HttpURLConnection conn = (HttpURLConnection)new URL(url).openConnection(); int httpStatus = conn.getResponseCode(); if (httpStatus != HttpURLConnection.HTTP_OK) { throw new IOException("HTTP status returned not OK but " + httpStatus); } InputStream is = conn.getInputStream(); File file = new File(id + ".html"); if (file.exists()) { throw new IllegalStateException("already exists : " + file); } OutputStream os = new FileOutputStream(file); try { byte[] buffer = new byte[8192]; for (int length; (length = is.read(buffer)) >= 0;) { os.write(buffer, 0, length); } } finally { os.close(); } Properties props = new Properties(); props.setProperty("id", id); props.setProperty("time", new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(now)); return props; } private static void outputResult(PrintWriter out) throws IOException { BufferedReader reader = new BufferedReader(new FileReader(FILE)); try { for (String line; (line = reader.readLine()) != null;) { out.println(line); } } finally { reader.close(); } } private static void appendRecord(Properties props) throws IOException { List<String> list = new ArrayList<String>(); if (!FILE.exists()) { FILE.createNewFile(); } BufferedReader reader = new BufferedReader(new FileReader(FILE)); try { for (String line; (line = reader.readLine()) != null;) { if (line.startsWith("<ul")) { list.add(line); } } System.out.println(list); } finally { reader.close(); } PrintWriter out = new PrintWriter(new FileWriter(FILE)); try { out.println("<html><body>"); out.println("<style>.time { font-size: 75%; }</style>"); out.println("<h1>Local Bookmarks</h1>"); out.print("<ul><li>"); out.printf("<a href=\"%s\">%s</a>", new File(".", props.getProperty("id") + ".html").getPath(), props.getProperty("title")); out.printf(" (<a href=\"%s\">source</a>)", props.getProperty("url")); out.printf(" <span class=\"time\">%s</span>", props.getProperty("time")); out.print("</li>"); out.print("<ul><li> "); out.print(props.getProperty("comment", "")); out.print("</li></ul>"); out.println("</ul>"); for (String record : list) { out.println(record); } out.println("</body></html>"); out.flush(); } finally { out.close(); } } private static Properties parseQuery(String query) throws UnsupportedEncodingException { Properties props = new Properties(); String[] a = query.split("\\?")[1].split("&"); for (int i = 0; i < a.length; i++) { String[] kv = a[i].split("="); if (kv.length >= 2) { String value = URLDecoder.decode(kv[1], CHARSET); props.setProperty(kv[0], value); } } System.out.println(props); return props; } public static void main(String[] args) throws IOException { int port = (args.length > 0) ? Integer.parseInt(args[0]) : 80; ServerSocket serverSocket = new ServerSocket(port); while (true) { Socket socket = serverSocket.accept(); try { InputStream is = socket.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(is)); String query = ""; String referer = ""; while (true) { String line = reader.readLine(); if (line == null || line.length() == 0) { break; } System.out.println(line); if (line.startsWith("GET")) { query = line.split(" ")[1]; } else if (line.startsWith("Referer:")) { referer = line.split(" ")[1]; } } PrintWriter out = new PrintWriter(socket.getOutputStream()); out.println("HTTP/1.1 200 OK"); out.println("Content-type: text/html;charset=" + CHARSET); out.println(); if (query.startsWith("/register")) { Properties params = parseQuery(query); Properties props = getRemoteFiles(params.getProperty("url")); props.putAll(params); appendRecord(props); outputResult(out); } else if (query.startsWith("/edit")) { out.println(); outputForm(out, referer); } else { outputResult(out); } out.flush(); } finally { socket.close(); } } } }