再帰検索+テキストデータの連結
WSDLより、Javaソースを作成すると、web.xmlとかがWSDLファイル数分できました。
で、それらを連結しなくちゃいけないんだけど、ファイル数が20ぐらいあるので、プログラムを組んでみました。
ただ、web.xmlの宣言部やROOT要素は共通なので、不要。
よって、web.xmlの1,2,最終行は連結しない。
以下、ソース。
import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.List; public class Concat { /** 指定フォルダ用定数 */ private static final String FOLDER_PATH = "C:\\Users\\Public\\Desktop\\Test"; /** 指定ファイル名用定数 */ private static final String FILE_NAME = "web.xml"; /** * メイン処理。 * * @param args 使用しません * @throws Exception 実行時例外 */ public static void main(String[] args) throws Exception { // 指定フォルダ File file = new File(FOLDER_PATH); // ファイルリストの取得 List<File> list = getFile(file, FILE_NAME); BufferedWriter bw = null; try { bw = new BufferedWriter(new FileWriter(FILE_NAME)); for (File f : list) { List<String> lines = getLineText(f); for (int i = 0; i < lines.size(); i++) { if (i == 0 || i == 1 || i == lines.size() - 1) { // 特定の行のみコピーしない continue; } bw.write(lines.get(i)); bw.newLine(); } } } finally { if (bw != null) { bw.close(); } } } /** * 指定ファイル内のテキストを、行単位でリストにし返す。 * * @param file 指定ファイル * @return 行単位のテキストデータ * @throws IOException 入出力例外 */ private static List<String> getLineText(File file) throws IOException { // 返却値 List<String> lines = new ArrayList<String>(); BufferedReader br = null; try { br = new BufferedReader(new FileReader(file)); String line = null; // ファイルが存在する間ループ while ((line = br.readLine()) != null) { lines.add(line); } } finally { if (br != null) { br.close(); } } return lines; } /** * ファイル名を指定し、指定フォルダ以下のファイルリストを再帰的に取得します。 * * @param folder 指定フォルダ * @param fileName ファイル名 * @return ファイルリスト */ private static List<File> getFile(File folder, String fileName) { // 返却値 List<File> list = new ArrayList<File>(); File[] files = folder.listFiles(); for (File file : files) { if (file.isDirectory()) { // 同一ファイル名を再帰検索 list.addAll(getFile(file, fileName)); continue; } if (file.getName().equals(fileName)) { // 同一ファイル名のパスを追加 list.add(file); } } return list; } }
getFileで再帰的にweb.xmlを検索。
mainのループの中でファイルのテキストデータを取得し、
その指定行数以外をReaderに出力。
って流れかな?
再帰って久々に使った・・・。
重複チェック
まずは、簡単なオブジェクトを定義
/** * ユーザクラス */ class User { /** ID */ private String id; /** 名前 */ private String name; /** メールアドレス */ private String email; (Getter, Setterなどが定義されている) }
で、単純にemailが重複していないかをチェックします。
class Test { public static void main(String[] args) { // メールアドレスset Set<String> set = new HashSet<String>(); List<User> userList = (いろいろなUserクラスが入っているとする。) for (User user : userList) { String bufEmail = user.getEmail(); if (set.contains(bufEmail)) { // 存在する場合 System.out.println(bufEmail + "が重複しています。"); } else { // 存在しない場合、setに追加 set.add(bufEmail); } } } }
次、どのidと重複しているかを表示する場合。
class Test2 { public static void main(String[] args) { // キー:email、値:idとなるmap Map<String, String> map = new HashMap<String, String>(); List<User> userList = (いろいろなUserクラスが入っているとする。) for (User user : userList) { String bufEmail = user.getEmail(); String tgtId = map.get(bufEmail); if (tgtId != null) { // 存在した場合 System.out.println(bufEmail + "が" + tgtId + "と重複しています。"); } else { // 存在しなかった場合、mapに追加 map.put(bufEmail, user.getId()); } } } }
以上、変数名などテキトー。
また、Eclipseがやる気を起こしてくれないため、手書き+未コンパイル。
動作確認なし。
間違ってたら、そのうち直そうと思います。
SimpleDateFormatについて
文字列strがあり、
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd"); sdf.parse(str);
とした場合、
"2010/03/18"を与えると正常に動作しますが、
"2010/03/1a"を与えた場合も例外が発生しません。
取得できるデータは2020年3月1日となります。
エラーチェックとしてparseしてみる場合も、上記のような場合はエラーとならないので、ご注意ください。
Google Web Toolkit覚書(GAEプラグイン)
流れ
Eclipseにて「Webアプリケーション・プロジェクト」を作成したら、基本的なファイルは作られる。
プロジェクトにて右クリック、実行、Webアプリケーションを選択すると、実行できる。
プロジェクト作成時に(プロジェクト名を"Sample"とする。)
war/Sample.html
が作成される。
tdタグなどにid属性をつけておく。(Javaよりそのid値を利用しアクセスする)
src/パッケージ/Sample.gwt.xml
が何者かによって呼び出される(はず)
entry-pointタグのclass属性クラスがロードされる。
そのクラスがEntryPointインターフェースを実装しており、onModuleLoadが呼び出される。
final TextBox nameField = new TextBox(); RootPanel.get("nameFieldContainer").add(nameField);
htmlのid値が"nameFieldContainer"となっているため、そのエレメント内部にTextBoxが追加される。
サーバとの通信
clientパッケージに、
GreetingServiceインターフェース
GreetingServiceAsyncインターフェース
が必要。(名前は*Service, *ServiceAsyncが妥当?)
命名規則や、シグネチャが間違っている場合は書いてる途中に、
「No asynchronous version of method メソッド名 exists in type GreetingServiceAsync」
とか言われるけど、インターフェースx2、実装クラスをちゃんと記述したらエラーは無くなる。
serverパッケージに
GreetingServiceImplクラス
が必要。(名前は*ServiceImplが妥当?)
client.GreetingServiceを実装する。(Asyncが無い方)
サーバー側は通常のJavaが利用できる。(好きに書いて良い)
クライント側はjava.lang以下+java.util以下のパッケージ以外は、googleパッケージや自作クラスのみ利用可能(?)
sharedパッケージ以下にはサーバ・クライアント両方で利用するクラスを定義する。
POJOなBeanやバリデータなどを定義するためのパッケージ?
→ただし、
・バリデータでも基本以外のクラスも利用できないので、簡単なチェックのみを記述し、実際のチェックはサーバサイドで行うべき(?)
・サーバ・クライアント間のデータを、POJOなBeanに詰めてやりとりする事は可能。ただし、それをそのまま永続化(DBに登録)はできない。(アノテーションを付けると、コンパイル時に怒られる→org.datanucleus.metadata.InvalidMetaDataException: Class com.noboru.shared.SampleModel has application-identity and no objectid-class specified yet has 0 primary key fields. Unable to use SingleFieldIdentity.)
どのパッケージをクライアント側とする(JavaScriptに変換する?)かは、Sample.gwt.xmlのsourceタグによる?
永続化(JDOの場合)
package com.sample.server; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import javax.jdo.annotations.IdGeneratorStrategy; import javax.jdo.annotations.IdentityType; import javax.jdo.annotations.PersistenceCapable; import javax.jdo.annotations.Persistent; import javax.jdo.annotations.PrimaryKey; import com.sample.shared.TransModel; @PersistenceCapable(identityType = IdentityType.APPLICATION) public class StoreModel { @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Long id; @Persistent private Date date; public StoreModel() { } public StoreModel(Date date, Integer amount, String store, String note) { this.date = date; } public StoreKakeiboModel(TransModel model) { SimpleDateFormat sdf = new SimpleDateFormat(); sdf.applyPattern("yyyy/MM/dd"); try { this.date = sdf.parse(model.getDate()); } catch (ParseException e) { e.printStackTrace(); this.date = null; } } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } }
上記のように、いくつかのアノテーションが必要。
@PersistenceCapable
@PrimaryKey
@Persistent
が必要っぽい。
また、お約束クラス(?)が必要。(無くても毎回書けば動く)
package com.sample.server; import javax.jdo.JDOHelper; import javax.jdo.PersistenceManagerFactory; public class PMF { private static final PersistenceManagerFactory pmfInstance = JDOHelper .getPersistenceManagerFactory("transactions-optional"); private PMF() { } public static PersistenceManagerFactory get() { return pmfInstance; } }
transactions-optionalは、META-INFのjdoconfig.xmlによる?
いろいろ設定できるみたいだけど、不明。
デフォルトのままで動くので放置。
インサート(登録)は以下の通り
package com.sample.server; import javax.jdo.PersistenceManager; import com.google.gwt.user.server.rpc.RemoteServiceServlet; import com.sample.client.GreetingService; import com.sample.shared.KakeiboModel; public class GreetingServiceImpl extends RemoteServiceServlet implements GreetingService { public void insertNew(TransModel model) { StoreModel newModel = new StoreModel(model); PersistenceManager pm = PMF.get().getPersistenceManager(); try { pm.makePersistent(newModel); } finally { pm.close(); } } }
PersistenceManager#makePersistentの引数に永続化対象クラスを渡すだけ。
更新も同様にできる。(らしい)
SELECT(照会)は以下の通り。
@SuppressWarnings("unchecked") public List<TransModel> listAll() { List<TransModel> ret = new ArrayList<TransModel>(); PersistenceManager pm = PMF.get().getPersistenceManager(); Query query = pm.newQuery(StoreModel.class); query.setOrdering("date asc"); List<StoreModel> list = (List<StoreModel>) query .execute(); for (StoreModel storedData : list) { TransModel model = new TransModel(); model.setId(Long.toString(storedData.getId())); model.setDate(Converter.dateToString(storedData.getDate())); ret.add(model); } return ret; }
ただ、サーバ側→クライアント側では、TransModelのコンストラクタにStoreModelを渡したりはできない。(serverパッケージをインポートできないため。)
サーバ側に、ファクトリークラスを作ったりしたらよい?
まとめ
GWTをやる気が薄れてきたので、文字として残しておく。
後で参考になるといいな。
日付をフォーマットする
DBより取得した値を、MM月dd日に変換する。
ただし、一桁の場合はスペース埋めを行う。
(ゼロ埋めなら簡単なのに・・・)
// DB取得値のつもり String dateStr = "2010-02-19 23:44:40"; // Dateに変換 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); Date date = sdf.parse(dateStr); // Calendarを取得 Calendar calendar = Calendar.getInstance(); calendar.setTime(date); // フォーマット int month = calendar.get(Calendar.MONTH) + 1; int day = calendar.get(Calendar.DAY_OF_MONTH); String formatDate = String.format("%2d月%2d日", month, day); // 出力 System.out.println(formatDate);
出力結果
2月19日
↑スペースがちゃんと出力されています。
もっと簡単にできると思うんだけど・・・
ちなみに、ゼロ埋めで良ければ
// フォーマット SimpleDateFormat sdf2 = new SimpleDateFormat("MM月dd日"); String formatDate2 = sdf2.format(date); // 出力 System.out.println(formatDate2);
出力結果
02月19日
ゼロ埋めならこんなに簡単。
仕様変更してくれんかなー。(たぶん無理)
google calendarに終日イベントを追加する
基本的に他の人のソースのパクリ。
package sample.calendar; import java.net.URL; import com.google.gdata.client.calendar.CalendarService; import com.google.gdata.data.DateTime; import com.google.gdata.data.PlainTextConstruct; import com.google.gdata.data.calendar.CalendarEventEntry; import com.google.gdata.data.extensions.When; public class AllDayEventCreator { // Google アカウント private static String GOOGLE_ACCOUNT = "xxxxxxxxxx@gmail.com"; // Google アカウントのパスワード private static String GOOGLE_PASSWORD = "xxxxxxxxxx"; // 送信URL private static String GOOGLE_CAL_URL = "http://www.google.com/calendar/feeds/default/private/full"; /** * 終日のイベントを追加します。 * * @throws Exception */ public void insertAlldayData() throws Exception { URL postURL = new URL(GOOGLE_CAL_URL); // イベント登録クラス CalendarEventEntry calEntry = new CalendarEventEntry(); // タイトルを設定 calEntry.setTitle(new PlainTextConstruct("終日のテスト")); // 詳細を設定 calEntry.setContent(new PlainTextConstruct("詳細のサンプル")); DateTime startTime = new DateTime(); startTime.setTzShift(9); startTime = DateTime.parseDate("2009-11-27"); // 開始終了日時をWhen型オブジェクトに代入し、イベントクラスに追加 When eventTimes = new When(); eventTimes.setStartTime(startTime); calEntry.addTime(eventTimes); // Google Calendarサービスに接続 CalendarService calService = new CalendarService( "chaos-alldaySample-0.1"); calService.setUserCredentials(GOOGLE_ACCOUNT, GOOGLE_PASSWORD); // スケジュールを追加する calService.insert(postURL, calEntry); System.out.println("終日の予定:「" + calEntry.getTitle().getPlainText() + "」を追加しました。"); } /** * @param args */ public static void main(String[] args) { try { // インスタンス生成 AllDayEventCreator main = new AllDayEventCreator(); // 終日イベントを追加 main.insertAlldayData(); } catch (Exception e) { e.printStackTrace(); } } }
終日イベントはsetStartTimeに渡すDateTimeを作成するときに、
DateTime.parseDate("2009-11-27");
のように、parseDateを使い、時分秒を指定しないとできる(みたい)。
google calendarでカレンダーを作成する
基本的にはsampleについてきたCalendarFeedDemo.javaから、
不要だと思われる箇所を削っただけ。
package sample.calendar; import java.net.URL; import com.google.gdata.client.calendar.CalendarService; import com.google.gdata.data.PlainTextConstruct; import com.google.gdata.data.calendar.CalendarEntry; import com.google.gdata.data.calendar.ColorProperty; import com.google.gdata.data.calendar.HiddenProperty; import com.google.gdata.data.calendar.TimeZoneProperty; import com.google.gdata.data.extensions.Where; public class CreateCalendar { /** google calendar 基準URL */ private static final String METAFEED_URL_BASE = "http://www.google.com/calendar/feeds/"; /** マイカレンダー用アドレス */ private static final String OWNCALENDARS_FEED_URL_SUFFIX = "/owncalendars/full"; /** マイカレンダーフィード用URL */ private static URL owncalendarsFeedUrl = null; private static final String BLUE = "#2952A3"; /** ユーザー名 */ static final String USER_NAME = "xxxxxxxx@gmail.com"; /** パスワード */ static final String USER_PASSWORD = "xxxxxxxx"; /** * カレンダーを作成します。 * * @param service * CalendarServiceオブジェクト * @return 作成されたCalendarEntryオブジェクト * @throws Exception */ private static CalendarEntry createCalendar(CalendarService service) throws Exception { System.out.println("カレンダーを作成します。"); // カレンダーオブジェクトを初期化 CalendarEntry calendar = new CalendarEntry(); calendar.setTitle(new PlainTextConstruct("マイカレンダー1")); calendar.setSummary(new PlainTextConstruct("サンプルの説明")); calendar.setTimeZone(new TimeZoneProperty("Asia/Tokyo")); calendar.setHidden(HiddenProperty.FALSE); calendar.setColor(new ColorProperty(BLUE)); calendar.addLocation(new Where("", "", "Toyama")); // カレンダーを追加します。 CalendarEntry newEntry = service.insert(owncalendarsFeedUrl, calendar); return newEntry; } public static void main(String[] args) { try { // URLオブジェクトを作成 owncalendarsFeedUrl = new URL(METAFEED_URL_BASE + USER_NAME + OWNCALENDARS_FEED_URL_SUFFIX); // カレンダーサービスに接続 CalendarService service = new CalendarService( "chaos-CreateCalendarSample-1"); service.setUserCredentials(USER_NAME, USER_PASSWORD); // カレンダーを作成 createCalendar(service); } catch (Exception e) { e.printStackTrace(); } } }
コンストラクタ:CalendarServiceの引数は文字列で
[company-id]-[app-name]-[app-version]
らしいので、テキトーに変更。
(例外処理とかはテキトーです。)
jsfのinputタグなどをJavaScriptで制御する。
<h:view> <h:form id="form1"> <h:inputText id="userId" /> </h:form> </h:view>
と、すると、inputタグは"form1:userId"というid(nameだったかも)になる。
ここで、
document.form1.form1:userId.value = "hoge";
としてもJavaScriptエラーとなる。
ので、
document.getElementById("form1:userId").value = "hoge";
や、
document.all['form1:userId'].value = 'hoge';
とする。
プロパティファイルを編集するeclipseプラグイン
忘れないようにメモ。
JSFのメッセージファイル(?)のようなプロパティファイルは、直接日本語を編集できないので、
PropertiesEditorプラグインを入れます。
http://sourceforge.jp/projects/propedit/files/
以上。