/*
** 指定したURLからhtmlページをダウンロードするサンプル
*/
application HtmlDownloader;
import FileSystem;
import NetworkSystem;
import FileStream;
import SocketStream;
import Dialogs;
import Form;
import Label;
import Button;
import TextField;
/*
URL解析失敗を表す例外
*/
exception URLParseFailure inherits ParseFailure;
/*
簡易URLクラス
https://hostname/path
hostname/path
の2形式にのみ対応したURL解析クラス。
*/
class URL is private :
for all :
// コンストラクタ
constructor new( url as String ) :
this.url = url;
parse;
end
// URLの文字列表現
field url as String;
// ホスト
field host as String;
// パス
field path as String;
// ホストとパスをまとめて返す
method parts as String, String :
return host, path;
end
for self :
constant HTTP_PROTOCOL_NAME as String = "https://";
// urlからhostとpathを抽出する
method parse :
var length = url.length;
var host_and_path as String;
if length >= HTTP_PROTOCOL_NAME.size and url.cut_bytes(0, HTTP_PROTOCOL_NAME.size) == HTTP_PROTOCOL_NAME :
var remain = url.size - HTTP_PROTOCOL_NAME.size;
if remain == 0 :
throw URLParseFailure.NoHost;
end
host_and_path = url.cut_bytes(HTTP_PROTOCOL_NAME.size, remain);
else :
host_and_path = url;
end
var slash_offset = host_and_path.find('/'@Byte);
if slash_offset == -1 :
throw URLParseFailure.NoPath;
else if slash_offset == 0 :
throw URLParseFailure.NoHost;
end
host = host_and_path.cut_bytes(0, slash_offset);
path = host_and_path.cut_bytes(slash_offset, host_and_path.size - slash_offset);
end
end
/*
HTTPでファイルを取得するルーチンクラス
ダウンロード処理を独立させるために作った。
*/
class HttpGetter :
for all :
constant USER_AGENT as String = "Http Getter(Uva Sample)/0.0";
// ダウンロード処理
own method download( host as String, path as String, file_path as String ) :
// 設定値
var port = 80;
//--- ソケットを開く
var socket = Socket.open(host, port);
var socket_out = SocketOutputStream.new(socket);
System.display.println("--- connected ---");
// リクエスト文字列の作成
var message = String.format(
"GET %1 HTTP/1.0\nUser-Agent: %2\n\n",
[path, USER_AGENT]
);
//--- リクエストの送信
System.display.println("--- request ---");
System.display.println(message);
socket_out.write(message);
//--- レスポンスの表示
System.display.println("--- response ---");
read_response(SocketInputStream.new(socket), file_path);
do :
var dlg = MessageDialog.new_information("Sample", "取得しました。");
dlg.execute;
end
catch NetworkSystemFailure :
var dlg = MessageDialog.new_error("Sample", Exception.description, MessageDialog.BUTTON_OK);
dlg.execute;
finally :
//--- ソケットを閉じる
Socket.close(socket);
end
for self :
// サーバーからの応答を処理する
own method read_response( input as InputStream, file_path as String ) :
var reader = TextInputStream.new(input);
var header_received = false;
var line = "";
// ヘッダをディスプレイに出力する
System.display.println("[header]");
while true :
line = reader.read_line;
break if line.length == 0;
System.display.println(line);
loop
// 内容をファイルに出力する
var file = FileOutputStream.open(file_path);
while true :
line = reader.read_line;
file.write(line);
file.write(System.EOL);
catch StreamTerminator :
loop
file.close;
catch FileSystemFailure :
var dlg = MessageDialog.new_error("Sample", Exception.description, MessageDialog.BUTTON_OK);
dlg.execute;
end
end
/*
アプリケーションの起動クラス
*/
class HtmlDownloader :
// 起動メソッド
method main :
// フォームを作成して
make_form;
// フォームをセンタリングして
form.move_to_center;
// フォームをモーダル表示する
form.show_modal;
end
for self :
// フォーム(メインウインドウ)
field form as Form;
// "URL"ラベル
field url_label as Label;
// URL入力フィールド
field url_field as TextField;
// 取得ボタンの作成
field go_button as PushButton;
// フォームを作成する
method make_form :
// フォームの作成
form = Form.new(this);
form.set_caption("Webページをダウンロード");
form.set_client_extent(400, 30);
form.set_resizable(false);
// "URL"ラベルの作成
url_label = Label.new(this);
url_label.set_bounds(5, 5, 60, 20);
url_label.set_caption("URL:");
// URL入力フィールドの作成
url_field = TextField.new(this);
url_field.set_bounds(65, 5, 256, 20);
url_field.set_text("www.yahoo.co.jp/index.html");
// 取得ボタンの作成
go_button = PushButton.new(this);
go_button.set_bounds(65+256+5, 5, 60, 20);
go_button.set_caption("Get!!");
go_button.set_action("download"); // アクション名を"download"としている
// フォームにウィジェットを追加する
form.add(url_label);
form.add(url_field);
form.add(go_button);
end
for self :
// 開始ボタンが押された時に発生するアクションのハンドラ
method download( source as Object ) :
var url = URL.new(url_field.text);
var file_dlg = FileDialog.new;
if file_dlg.execute(FileDialog.FOR_SAVE) :
var file_path = file_dlg.directory + FileSystem.FILENAME_SEPARATOR + file_dlg.file_name;
HttpGetter.download(url.host, url.path, file_path);
end
catch URLParseFailure :
var dlg = MessageDialog.new_error("Sample", "URLが正しくありません。", MessageDialog.BUTTON_OK);
dlg.execute;
end
end