OSDN Git Service

mavenによるビルドに変更
[charactermanaj/CharacterManaJ.git] / src / main / java / charactermanaj / model / io / CharacterDataDefaultProvider.java
1 package charactermanaj.model.io;\r
2 \r
3 import java.io.BufferedOutputStream;\r
4 import java.io.File;\r
5 import java.io.FileNotFoundException;\r
6 import java.io.FileOutputStream;\r
7 import java.io.IOException;\r
8 import java.io.InputStream;\r
9 import java.net.URI;\r
10 import java.net.URL;\r
11 import java.sql.Timestamp;\r
12 import java.util.EnumSet;\r
13 import java.util.Enumeration;\r
14 import java.util.LinkedHashMap;\r
15 import java.util.Map;\r
16 import java.util.Properties;\r
17 import java.util.logging.Level;\r
18 import java.util.logging.Logger;\r
19 \r
20 import charactermanaj.model.CharacterData;\r
21 import charactermanaj.util.ConfigurationDirUtilities;\r
22 import charactermanaj.util.LocalizedResourcePropertyLoader;\r
23 import charactermanaj.util.ResourceLoader;\r
24 import charactermanaj.util.ResourceNames;\r
25 import charactermanaj.util.SetupLocalization;\r
26 \r
27 /**\r
28  * デフォルトキャラクターセットのプロバイダ\r
29  * \r
30  * @author seraphy\r
31  */\r
32 public class CharacterDataDefaultProvider {\r
33 \r
34         /**\r
35          * リソースに格納されているデフォルトのキャラクター定義のリソースパスまでのプレフィックス.<br>\r
36          */\r
37         public static final String DEFAULT_CHARACTER_PREFIX = "template/";\r
38 \r
39         /**\r
40          * テンプレートをリストしているXML形式のプロパティファイル名\r
41          */\r
42         public static final String TEMPLATE_LIST_XML = "characterDataTemplates";\r
43 \r
44         /**\r
45          * デフォルトのキャラクターセット名(ver2)\r
46          */\r
47         public static final String DEFAULT_CHARACTER_NAME_V2 = "character2.xml";\r
48 \r
49         /**\r
50          * デフォルトのキャラクターセット名(ver3)\r
51          */\r
52         public static final String DEFAULT_CHARACTER_NAME_V3 = "character3.xml";\r
53 \r
54         /**\r
55          * ロガー\r
56          */\r
57         private static final Logger logger = Logger\r
58                         .getLogger(CharacterDataDefaultProvider.class.getName());\r
59 \r
60         public enum DefaultCharacterDataVersion {\r
61                 V2() {\r
62                         public String getResourceName() {\r
63                                 return DEFAULT_CHARACTER_NAME_V2;\r
64                         }\r
65                 },\r
66                 V3() {\r
67                         public String getResourceName() {\r
68                                 return DEFAULT_CHARACTER_NAME_V3;\r
69                         }\r
70                 };\r
71 \r
72                 public abstract String getResourceName();\r
73 \r
74                 public CharacterData create(CharacterDataDefaultProvider prov) {\r
75                         if (prov == null) {\r
76                                 throw new IllegalArgumentException();\r
77                         }\r
78                         try {\r
79                                 return prov.loadPredefinedCharacterData(getResourceName());\r
80 \r
81                         } catch (IOException ex) {\r
82                                 throw new RuntimeException(\r
83                                                 "can not create the default profile from application's resource",\r
84                                                 ex);\r
85                         }\r
86                 }\r
87         }\r
88 \r
89         /**\r
90          * デフォルトのキャラクター定義を生成して返す.<br>\r
91          * 一度生成された場合はキャッシュされる.<br>\r
92          * 生成されたキャラクター定義のdocBaseはnullであるため、docBaseをセットすること.<br>\r
93          * \r
94          * @param version\r
95          *            デフォルトキャラクターセットのバージョン\r
96          * @return キャラクター定義\r
97          */\r
98         public synchronized CharacterData createDefaultCharacterData(\r
99                         DefaultCharacterDataVersion version) {\r
100                 if (version == null) {\r
101                         throw new IllegalArgumentException();\r
102                 }\r
103                 return version.create(this);\r
104         }\r
105 \r
106         /**\r
107          * テンプレートリストの定義プロパティを読み込む.<br>\r
108          * neutral引数がfalseの場合は現在のロケールを優先する.<br>\r
109          * 引数がtrueの場合は読み込み順を逆転させ、言語中立を優先する.<br>\r
110          * \r
111          * @param neutral\r
112          *            言語中立を優先する場合\r
113          * @return テンプレートリストのプロパティ\r
114          */\r
115         private Properties getTemplateListProperties(boolean neutral) {\r
116                 // テンプレートリソースは実行中に増減する可能性があるため、\r
117                 // 共有キャッシュには入れない.\r
118                 LocalizedResourcePropertyLoader loader = new LocalizedResourcePropertyLoader(\r
119                                 null);\r
120                 String name = DEFAULT_CHARACTER_PREFIX + TEMPLATE_LIST_XML;\r
121                 ResourceNames resNames = LocalizedResourcePropertyLoader\r
122                                 .getResourceNames(name, null);\r
123                 if (neutral) {\r
124                         // 言語中立を優先する場合は、プロパティの重ね順を逆転させて言語中立で最後に上書きする.\r
125                         resNames = resNames.reverse();\r
126                 }\r
127                 return loader.getLocalizedProperties(name);\r
128         }\r
129 \r
130         /**\r
131          * キャラクターデータのxmlファイル名をキーとし、表示名を値とするマップ.<br>\r
132          * 表示順序でアクセス可能.<br>\r
133          * \r
134          * @return 順序付マップ\r
135          */\r
136         public Map<String, String> getCharacterDataTemplates() {\r
137                 // キャラクターデータのxmlファイル名をキーとし、表示名を値とするマップ\r
138                 final LinkedHashMap<String, String> templateNameMap = new LinkedHashMap<String, String>();\r
139 \r
140                 // テンプレートの定義プロパティのロード\r
141                 Properties props = getTemplateListProperties(false);\r
142 \r
143                 // 順序優先\r
144                 String strOrders = props.getProperty("displayOrder");\r
145                 if (strOrders != null) {\r
146                         for (String key : strOrders.split(",")) {\r
147                                 key = key.trim();\r
148                                 String val = props.getProperty(key);\r
149                                 if (val != null && val.trim().length() > 0) {\r
150                                         String resKey = DEFAULT_CHARACTER_PREFIX + key;\r
151                                         if (getResource(resKey) != null) {\r
152                                                 // 現存するテンプレートのみ登録\r
153                                                 templateNameMap.put(key, val);\r
154                                         }\r
155                                 }\r
156                         }\r
157                 }\r
158 \r
159                 // 順序が指定されていないアイテムの追加\r
160                 Enumeration<?> enm = props.propertyNames();\r
161                 while (enm.hasMoreElements()) {\r
162                         String key = (String) enm.nextElement();\r
163                         String val = props.getProperty(key);\r
164                         if (key.endsWith(".xml")) {\r
165                                 String resKey = DEFAULT_CHARACTER_PREFIX + key;\r
166                                 if (getResource(resKey) != null) {\r
167                                         // 現存するテンプレートのみ登録\r
168                                         templateNameMap.put(key, val);\r
169                                 }\r
170                         }\r
171                 }\r
172 \r
173                 // フォルダにある未登録のxmlファイルもテンプレート一覧に加える\r
174                 // (ただし、テンプレートリストプロパティを除く)\r
175                 try {\r
176                         File templDir = getTemplateDir(false);\r
177                         if (templDir.isDirectory()) {\r
178                                 File[] files = templDir.listFiles(new java.io.FileFilter() {\r
179                                         public boolean accept(File pathname) {\r
180                                                 String name = pathname.getName();\r
181                                                 if (templateNameMap.containsKey(name)) {\r
182                                                         // すでに登録済みなのでスキップする.\r
183                                                         return false;\r
184                                                 }\r
185                                                 if (name.startsWith(TEMPLATE_LIST_XML)) {\r
186                                                         // テンプレートリストプロパティファイルは除外する.\r
187                                                         return false;\r
188                                                 }\r
189                                                 return pathname.isFile() && name.endsWith(".xml");\r
190                                         }\r
191                                 });\r
192                                 if (files == null) {\r
193                                         files = new File[0];\r
194                                 }\r
195                                 CharacterDataPersistent persist = CharacterDataPersistent\r
196                                                 .getInstance();\r
197                                 for (File file : files) {\r
198                                         try {\r
199                                                 URI docBase = file.toURI();\r
200                                                 CharacterData cd = persist.loadProfile(docBase);\r
201                                                 if (cd != null && cd.isValid()) {\r
202                                                         String name = file.getName();\r
203                                                         templateNameMap.put(name, cd.getName());\r
204                                                 }\r
205                                         } catch (IOException ex) {\r
206                                                 logger.log(Level.WARNING, "failed to read templatedir."\r
207                                                                 + file, ex);\r
208                                         }\r
209                                 }\r
210                         }\r
211 \r
212                 } catch (IOException ex) {\r
213                         // ディレクトリの一覧取得に失敗しても無視する.\r
214                         logger.log(Level.FINE, "failed to read templatedir.", ex);\r
215                 }\r
216 \r
217                 return templateNameMap;\r
218         }\r
219 \r
220         /**\r
221          * XMLリソースファイルから、定義済みのキャラクターデータを生成して返す.<br>\r
222          * (現在のロケールの言語に対応するデータを取得し、なければ最初の言語で代替する.)<br>\r
223          * 生成されたキャラクター定義のdocBaseはnullであるため、使用する場合はdocBaseをセットすること.<br>\r
224          * 都度、XMLファイルから読み込まれる.<br>\r
225          * \r
226          * @return デフォルトキャラクターデータ\r
227          * @throws IOException\r
228          *             失敗\r
229          */\r
230         public CharacterData loadPredefinedCharacterData(String name)\r
231                         throws IOException {\r
232                 CharacterData cd;\r
233                 String resKey = DEFAULT_CHARACTER_PREFIX + name;\r
234                 URL predefinedCharacter = getResource(resKey);\r
235                 if (predefinedCharacter == null) {\r
236                         throw new FileNotFoundException(resKey);\r
237                 }\r
238                 InputStream is = predefinedCharacter.openStream();\r
239                 try {\r
240                         logger.log(Level.INFO, "load a predefined characterdata. resKey="\r
241                                         + resKey);\r
242                         CharacterDataXMLReader characterDataXmlReader = new CharacterDataXMLReader();\r
243                         cd = characterDataXmlReader.loadCharacterDataFromXML(is, null);\r
244 \r
245                 } finally {\r
246                         is.close();\r
247                 }\r
248                 return cd;\r
249         }\r
250 \r
251         /**\r
252          * リソースを取得する.<br>\r
253          * \r
254          * @param resKey\r
255          *            リソースキー\r
256          * @return リソース、なければnull\r
257          */\r
258         protected URL getResource(String resKey) {\r
259                 ResourceLoader resourceLoader = new ResourceLoader();\r
260                 return resourceLoader.getResource(resKey);\r
261         }\r
262 \r
263         /**\r
264          * カスタマイズ用のテンプレートディレクトリを取得する.<br>\r
265          * まだ作成されていない場合で、prepareが指示されている場合はフォルダを準備し、 既定のファイルを作成する.<br>\r
266          * \r
267          * @param prepare\r
268          *            実際にディレクトリを準備する場合はtrue\r
269          * @return テンプレートディレクトリ\r
270          */\r
271         public File getTemplateDir(boolean prepare) throws IOException {\r
272                 File baseDir = ConfigurationDirUtilities.getUserDataDir();\r
273                 SetupLocalization setup = new SetupLocalization(baseDir);\r
274                 File resourceDir = setup.getResourceDir();\r
275 \r
276                 if (prepare) {\r
277                         // テンプレートリソースが未設定であれば設定する.\r
278                         setup.setupToLocal(\r
279                                         EnumSet.of(SetupLocalization.Resources.Template), false);\r
280 \r
281                         // ディレクトリがなければ作成しておく\r
282                         if (resourceDir.exists()) {\r
283                                 resourceDir.mkdirs();\r
284                         }\r
285                 }\r
286                 return new File(resourceDir, DEFAULT_CHARACTER_PREFIX);\r
287         }\r
288 \r
289         /**\r
290          * "characterDataTemplates*.xml"のファイルは管理ファイルのため、 <br>\r
291          * ユーザによる書き込みは禁止とする.<br>\r
292          * \r
293          * @param name\r
294          * @return 書き込み可能であるか?\r
295          */\r
296         public boolean canFileSave(String name) {\r
297                 if (name.trim().startsWith("characterDataTemplates")) {\r
298                         return false;\r
299                 }\r
300                 return true;\r
301         }\r
302 \r
303         /**\r
304          * 指定したキャラクターデータをテンプレートとして保存する.<br>\r
305          * \r
306          * @param name\r
307          *            保存するテンプレートファイル名\r
308          * @param cd\r
309          *            キャラクターデータ\r
310          * @param localizedName\r
311          *            表示名\r
312          * @throws IOException\r
313          */\r
314         public void saveTemplate(String name, CharacterData cd, String localizedName)\r
315                         throws IOException {\r
316                 if (name == null || !canFileSave(name)) {\r
317                         throw new IllegalArgumentException();\r
318                 }\r
319 \r
320                 // テンプレートファイル位置の準備\r
321                 // (リソースが、まだファイルに展開されていなれば展開する)\r
322                 File templDir = getTemplateDir(true);\r
323                 File templFile = new File(templDir, name);\r
324 \r
325                 // キャラクターデータをXML形式でテンプレートファイルへ保存\r
326                 CharacterDataXMLWriter characterDataXmlWriter = new CharacterDataXMLWriter();\r
327                 BufferedOutputStream bos = new BufferedOutputStream(\r
328                                 new FileOutputStream(templFile));\r
329                 try {\r
330                         // パーツセットなしの状態とし、名前をローカライズ名に設定する.\r
331                         CharacterData templCd = cd.duplicateBasicInfo(false);\r
332                         templCd.setName(localizedName);\r
333 \r
334                         characterDataXmlWriter.writeXMLCharacterData(templCd, bos);\r
335 \r
336                 } finally {\r
337                         bos.close();\r
338                 }\r
339 \r
340                 // テンプレートの定義プロパティのロード(言語中立を優先)\r
341                 Properties neutralProps = getTemplateListProperties(true);\r
342 \r
343                 // テンプレート一覧の更新\r
344                 neutralProps.put(name, localizedName);\r
345 \r
346                 // テンプレート一覧の保存\r
347                 File neutralPropsFile = new File(templDir, TEMPLATE_LIST_XML + ".xml");\r
348                 BufferedOutputStream fos = new BufferedOutputStream(\r
349                                 new FileOutputStream(neutralPropsFile));\r
350                 try {\r
351                         neutralProps.storeToXML(fos,\r
352                                         new Timestamp(System.currentTimeMillis()).toString());\r
353 \r
354                 } finally {\r
355                         bos.close();\r
356                 }\r
357         }\r
358 }\r