1 package charactermanaj.model.io;
\r
3 import java.awt.Color;
\r
4 import java.awt.image.BufferedImage;
\r
5 import java.io.ByteArrayOutputStream;
\r
7 import java.io.FileFilter;
\r
8 import java.io.FileInputStream;
\r
9 import java.io.FileOutputStream;
\r
10 import java.io.IOException;
\r
11 import java.io.InputStream;
\r
12 import java.io.OutputStream;
\r
13 import java.io.OutputStreamWriter;
\r
14 import java.net.URI;
\r
15 import java.net.URL;
\r
16 import java.nio.charset.Charset;
\r
17 import java.text.SimpleDateFormat;
\r
18 import java.util.ArrayList;
\r
19 import java.util.Collection;
\r
20 import java.util.Collections;
\r
21 import java.util.Date;
\r
22 import java.util.HashMap;
\r
23 import java.util.Iterator;
\r
24 import java.util.LinkedList;
\r
25 import java.util.List;
\r
26 import java.util.Locale;
\r
27 import java.util.NoSuchElementException;
\r
28 import java.util.concurrent.ExecutionException;
\r
29 import java.util.concurrent.ExecutorService;
\r
30 import java.util.concurrent.Executors;
\r
31 import java.util.concurrent.Future;
\r
32 import java.util.concurrent.TimeUnit;
\r
33 import java.util.concurrent.TimeoutException;
\r
34 import java.util.concurrent.atomic.AtomicBoolean;
\r
35 import java.util.logging.Level;
\r
36 import java.util.logging.Logger;
\r
38 import javax.xml.XMLConstants;
\r
39 import javax.xml.namespace.NamespaceContext;
\r
40 import javax.xml.parsers.DocumentBuilder;
\r
41 import javax.xml.parsers.DocumentBuilderFactory;
\r
42 import javax.xml.parsers.ParserConfigurationException;
\r
43 import javax.xml.parsers.SAXParser;
\r
44 import javax.xml.parsers.SAXParserFactory;
\r
45 import javax.xml.transform.OutputKeys;
\r
46 import javax.xml.transform.Transformer;
\r
47 import javax.xml.transform.TransformerConfigurationException;
\r
48 import javax.xml.transform.TransformerException;
\r
49 import javax.xml.transform.TransformerFactory;
\r
50 import javax.xml.transform.dom.DOMSource;
\r
51 import javax.xml.transform.stream.StreamResult;
\r
52 import javax.xml.validation.Schema;
\r
53 import javax.xml.validation.SchemaFactory;
\r
54 import javax.xml.xpath.XPath;
\r
55 import javax.xml.xpath.XPathFactory;
\r
57 import org.w3c.dom.Attr;
\r
58 import org.w3c.dom.Document;
\r
59 import org.w3c.dom.Element;
\r
60 import org.w3c.dom.Node;
\r
61 import org.w3c.dom.NodeList;
\r
62 import org.xml.sax.Attributes;
\r
63 import org.xml.sax.ErrorHandler;
\r
64 import org.xml.sax.SAXException;
\r
65 import org.xml.sax.SAXParseException;
\r
66 import org.xml.sax.helpers.DefaultHandler;
\r
68 import charactermanaj.graphics.io.FileImageResource;
\r
69 import charactermanaj.graphics.io.ImageLoader;
\r
70 import charactermanaj.graphics.io.ImageSaveHelper;
\r
71 import charactermanaj.graphics.io.LoadedImage;
\r
72 import charactermanaj.model.AppConfig;
\r
73 import charactermanaj.model.CharacterData;
\r
74 import charactermanaj.model.Layer;
\r
75 import charactermanaj.model.PartsAuthorInfo;
\r
76 import charactermanaj.model.PartsCategory;
\r
77 import charactermanaj.model.PartsManageData;
\r
78 import charactermanaj.model.PartsManageData.PartsKey;
\r
79 import charactermanaj.ui.MainFrame;
\r
80 import charactermanaj.util.DirectoryConfig;
\r
81 import charactermanaj.util.FileNameNormalizer;
\r
82 import charactermanaj.util.FileUserData;
\r
83 import charactermanaj.util.UserData;
\r
85 public class CharacterDataPersistent {
\r
90 public static final String CONFIG_FILE = "character.xml";
\r
93 * キャラクターなんとか機用のiniファイル名
\r
95 public static final String COMPATIBLE_CONFIG_NAME = "character.ini";
\r
100 private static final Logger logger = Logger
\r
101 .getLogger(CharacterDataPersistent.class.getName());
\r
104 * キャラクターデータを格納したXMLのリーダー
\r
106 private final CharacterDataXMLReader characterDataXmlReader = new CharacterDataXMLReader();
\r
108 private final CharacterDataXMLWriter characterDataXmlWriter = new CharacterDataXMLWriter();
\r
113 public static final String SAMPLE_IMAGE_FILENAME = "preview.png";
\r
118 public static final String VERSION_SIG_1_0 = "1.0";
\r
121 * キャラクター定義XMLファイルの名前空間
\r
123 public static final String NS = "http://charactermanaj.sourceforge.jp/schema/charactermanaj";
\r
126 * パーツ定義XMLファイルの名前空間
\r
128 public static final String NS_PARTSDEF = "http://charactermanaj.sourceforge.jp/schema/charactermanaj-partsdef";
\r
131 * キャラクター定義XML用のスキーマ定義リソース名
\r
133 private static final String CHARACTER_XML_SCHEMA = "/schema/character.xsd";
\r
136 * キャラクター定義XML用のスキーマ定義リソース名
\r
138 private static final String CHARACTER_XML_SCHEMA_0_8 = "/schema/0.8/character.xsd";
\r
141 * パーツセット定義XMLのスキーマ定義リソース名
\r
143 private static final String PARTSSET_XML_SCHEMA = "/schema/partsset.xsd";
\r
146 * パーツセット定義XMLのスキーマ定義リソース名
\r
148 private static final String PARTSSET_XML_SCHEMA_0_8 = "/schema/0.8/partsset.xsd";
\r
152 * SchemaのバリデージョンチェックとDOMの解析を始める前にSAXで流し込んで、
\r
153 * 最初のエレメント名や使用している名前空間、バージョンを読み込んで、 スキーマをきりかえられるようにするためのもの.
\r
157 public static class DocInfo {
\r
159 private String firstElementName;
\r
161 private String version;
\r
163 private String namespace;
\r
165 public void setFirstElementName(String firstElementName) {
\r
166 this.firstElementName = firstElementName;
\r
174 public String getFirstElementName() {
\r
175 return firstElementName;
\r
178 public void setNamespace(String namespace) {
\r
179 this.namespace = namespace;
\r
182 public void setVersion(String version) {
\r
183 this.version = version;
\r
187 * 最初の要素に指定されているxmlns属性の値
\r
191 public String getNamespace() {
\r
196 * 最初の要素に指定されているversion属性の値
\r
200 public String getVersion() {
\r
205 public String toString() {
\r
206 return firstElementName + " /version: " + version + " /namespace:"
\r
212 * プロファイルの列挙時のエラーハンドラ.<br>
\r
216 public interface ProfileListErrorHandler {
\r
226 void occureException(File baseDir, Throwable ex);
\r
230 * プロファイル列挙時のデフォルトのエラーハンドラ.<br>
\r
231 * 標準エラー出力にメッセージをプリントするだけで何もしない.<br>
\r
233 public static final ProfileListErrorHandler DEFAULT_ERROR_HANDLER = new ProfileListErrorHandler() {
\r
234 public void occureException(File baseDir, Throwable ex) {
\r
235 logger.log(Level.WARNING, "invalid profile. :" + baseDir, ex);
\r
242 private HashMap<String, Schema> schemaMap = new HashMap<String, Schema>();
\r
245 * JAXPで使用するデフォルトのエラーハンドラ
\r
247 private static final ErrorHandler errorHandler = new ErrorHandler() {
\r
248 public void error(SAXParseException exception) throws SAXException {
\r
251 public void fatalError(SAXParseException exception) throws SAXException {
\r
254 public void warning(SAXParseException exception) throws SAXException {
\r
260 * プライベートコンストラクタ.<br>
\r
261 * シングルトン実装であるため、一度だけ呼び出される.
\r
263 private CharacterDataPersistent() {
\r
270 private static final CharacterDataPersistent singleton = new CharacterDataPersistent();
\r
277 public static CharacterDataPersistent getInstance() {
\r
282 * キャラクターデータを新規に保存する.<br>
\r
283 * REVがnullである場合は保存に先立ってランダムにREVが設定される.<br>
\r
284 * 保存先ディレクトリはユーザー固有のキャラクターデータ保存先のディレクトリにキャラクター定義のIDを基本とする ディレクトリを作成して保存される.<br>
\r
285 * ただし、そのディレクトリがすでに存在する場合はランダムな名前で決定される.<br>
\r
286 * 実際のxmlの保存先にあわせてDocBaseが設定されて返される.<br>
\r
288 * @param characterData
\r
289 * キャラクターデータ (IDは設定済みであること.それ以外はvalid, editableであること。)
\r
290 * @throws IOException
\r
293 public void createProfile(CharacterData characterData) throws IOException {
\r
294 if (characterData == null) {
\r
295 throw new IllegalArgumentException();
\r
298 String id = characterData.getId();
\r
299 if (id == null || id.trim().length() == 0) {
\r
300 throw new IOException("missing character-id:" + characterData);
\r
303 // ユーザー個別のキャラクターデータ保存先ディレクトリを取得
\r
304 DirectoryConfig dirConfig = DirectoryConfig.getInstance();
\r
305 File charactersDir = dirConfig.getCharactersDir();
\r
306 if (!charactersDir.exists()) {
\r
307 if (!charactersDir.mkdirs()) {
\r
308 throw new IOException("can't create the characters directory. "
\r
312 if (logger.isLoggable(Level.FINE)) {
\r
313 logger.log(Level.FINE, "check characters-dir: " + charactersDir
\r
314 + ": exists=" + charactersDir.exists());
\r
317 // 新規に保存先ディレクトリを作成.
\r
318 // 同じ名前のディレクトリがある場合は日付+連番をつけて衝突を回避する
\r
319 File baseDir = null;
\r
320 String suffix = "";
\r
321 String name = characterData.getName();
\r
322 if (name == null) {
\r
323 // 表示名が定義されていなければIDで代用する.(IDは必須)
\r
324 name = characterData.getId();
\r
326 for (int retry = 0;; retry++) {
\r
327 baseDir = new File(charactersDir, name + suffix);
\r
328 if (!baseDir.exists()) {
\r
332 throw new IOException("character directory conflict.:"
\r
336 suffix = generateSuffix(retry);
\r
338 if (!baseDir.exists()) {
\r
339 if (!baseDir.mkdirs()) {
\r
340 throw new IOException("can't create directory. " + baseDir);
\r
342 logger.log(Level.INFO, "create character-dir: " + baseDir);
\r
346 File characterPropXML = new File(baseDir, CONFIG_FILE);
\r
347 if (characterPropXML.exists() && !characterPropXML.isFile()) {
\r
348 throw new IOException("character.xml is not a regular file.:"
\r
349 + characterPropXML);
\r
351 if (characterPropXML.exists() && !characterPropXML.canWrite()) {
\r
352 throw new IOException("character.xml is not writable.:"
\r
353 + characterPropXML);
\r
356 // DocBaseを実際の保存先に更新
\r
357 URI docBase = characterPropXML.toURI();
\r
358 characterData.setDocBase(docBase);
\r
360 // リビジョンが指定されてなければ新規にリビジョンを割り当てる。
\r
361 if (characterData.getRev() == null) {
\r
362 characterData.setRev(generateRev());
\r
366 saveCharacterDataToXML(characterData);
\r
369 preparePartsDir(characterData);
\r
375 * @return リビジョン用文字列
\r
377 public String generateRev() {
\r
378 SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd_HHmmss");
\r
379 return fmt.format(new Date());
\r
385 * @param retryCount
\r
389 protected String generateSuffix(int retryCount) {
\r
390 SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd_HHmmss");
\r
391 String suffix = "_" + fmt.format(new Date());
\r
392 if (retryCount > 0) {
\r
393 suffix = suffix + "_" + retryCount;
\r
401 * @param characterData
\r
402 * キャラクターデータ(有効かつ編集可能であること)
\r
403 * @throws IOException
\r
406 public void updateProfile(CharacterData characterData) throws IOException {
\r
407 if (characterData == null) {
\r
408 throw new IllegalArgumentException();
\r
411 characterData.checkWritable();
\r
412 if (!characterData.isValid()) {
\r
413 throw new IOException("invalid profile: " + characterData);
\r
417 saveCharacterDataToXML(characterData);
\r
420 preparePartsDir(characterData);
\r
424 * キャラクターデータのパーツイメージを保存するディレクトリを準備する
\r
426 * @param characterData
\r
430 * @throws IOException
\r
433 protected void preparePartsDir(CharacterData characterData)
\r
434 throws IOException {
\r
435 if (characterData == null) {
\r
436 throw new IllegalArgumentException();
\r
439 characterData.checkWritable();
\r
440 if (!characterData.isValid()) {
\r
441 throw new IOException("invalid profile: " + characterData);
\r
444 URI docBase = characterData.getDocBase();
\r
445 if (!"file".equals(docBase.getScheme())) {
\r
446 throw new IOException("ファイル以外はサポートしていません。:" + docBase);
\r
448 File docBaseFile = new File(docBase);
\r
449 File baseDir = docBaseFile.getParentFile();
\r
451 if (!baseDir.exists()) {
\r
452 if (!baseDir.mkdirs()) {
\r
453 throw new IOException("can't create directory. " + baseDir);
\r
456 for (PartsCategory partsCategory : characterData.getPartsCategories()) {
\r
457 for (Layer layer : partsCategory.getLayers()) {
\r
458 File layerDir = new File(baseDir, layer.getDir());
\r
459 if (!layerDir.exists()) {
\r
460 if (!layerDir.mkdirs()) {
\r
461 throw new IOException("can't create directory. "
\r
470 * キャラクターデータを読み込んだ場合に返されるコールバック
\r
472 public interface ListProfileCallback {
\r
475 * キャラクターデータを読み込んだ場合.<br>
\r
476 * 戻り値がfalseの場合は読み込みを以降の読み込みを中断します.<br>
\r
477 * (ただし、すでに読み込み開始している分については中断されません.)
\r
479 * @param characterData
\r
480 * @return 継続する場合はtrue、中止する場合はfalse
\r
482 boolean receiveCharacterData(CharacterData characterData);
\r
485 * キャラクターデータの読み込みに失敗した場合.<br>
\r
486 * 戻り値がfalseの場合は読み込みを以降の読み込みを中断します.<br>
\r
487 * (ただし、すでに読み込み開始している分については中断されません.)
\r
493 * @return 継続する場合はtrue、中止する場合はfalse
\r
495 boolean occureException(File dir, Exception ex);
\r
499 * キャラクターデータを非同期に読み込む.<br>
\r
500 * 読み込み完了したものが随時、コールバックに渡される.
\r
503 * @return すべての読み込みが完了したか判定し待機することのできるFuture
\r
505 public Future<?> listProfileAsync(final ListProfileCallback callback) {
\r
506 if (callback == null) {
\r
507 throw new IllegalArgumentException();
\r
510 // キャラクターデータが格納されている親ディレクトリのリスト
\r
511 DirectoryConfig dirConfig = DirectoryConfig.getInstance();
\r
512 File[] baseDirs = {dirConfig.getCharactersDir(),};
\r
515 FileNameNormalizer normalizer = FileNameNormalizer.getDefault();
\r
518 final AtomicBoolean cancelled = new AtomicBoolean(false);
\r
520 // 有効な論理CPU(CORE)数のスレッドで同時実行させる
\r
521 int numOfProcessors = Runtime.getRuntime().availableProcessors();
\r
522 final ExecutorService executorSrv = Executors
\r
523 .newFixedThreadPool(numOfProcessors);
\r
525 // キャラクターデータ対象ディレクトリを列挙し、並列に解析する
\r
526 for (File baseDir : baseDirs) {
\r
527 if (baseDir == null || !baseDir.exists()
\r
528 || !baseDir.isDirectory()) {
\r
531 for (File dir : baseDir.listFiles(new FileFilter() {
\r
532 public boolean accept(File pathname) {
\r
533 boolean accept = pathname.isDirectory()
\r
534 && !pathname.getName().startsWith(".");
\r
536 File configFile = new File(pathname, CONFIG_FILE);
\r
537 accept = configFile.exists()
\r
538 && configFile.canRead();
\r
543 String path = normalizer.normalize(dir.getPath());
\r
544 final File normDir = new File(path);
\r
546 executorSrv.submit(new Runnable() {
\r
547 public void run() {
\r
548 boolean terminate = false;
\r
549 File characterDataXml = new File(normDir,
\r
551 if (characterDataXml.exists()) {
\r
553 File docBaseFile = new File(normDir,
\r
555 URI docBase = docBaseFile.toURI();
\r
556 CharacterData characterData = loadProfile(docBase);
\r
557 terminate = !callback
\r
558 .receiveCharacterData(characterData);
\r
560 } catch (Exception ex) {
\r
561 terminate = !callback.occureException(
\r
566 // 中止が指示されたらスレッドプールを終了する
\r
567 logger.log(Level.FINE,
\r
568 "shutdownNow listProfile");
\r
569 executorSrv.shutdownNow();
\r
570 cancelled.set(true);
\r
577 // タスクの登録を受付終了し、現在のすべてのタスクが完了したらスレッドは終了する.
\r
578 executorSrv.shutdown();
\r
581 // タスクの終了を待機できる疑似フューチャーを作成する.
\r
582 Future<Object> awaiter = new Future<Object>() {
\r
583 public boolean cancel(boolean mayInterruptIfRunning) {
\r
584 if (executorSrv.isTerminated()) {
\r
588 executorSrv.shutdownNow();
\r
589 cancelled.set(true);
\r
593 public boolean isCancelled() {
\r
594 return cancelled.get();
\r
597 public boolean isDone() {
\r
598 return executorSrv.isTerminated();
\r
601 public Object get() throws InterruptedException, ExecutionException {
\r
603 return get(300, TimeUnit.SECONDS);
\r
605 } catch (TimeoutException ex) {
\r
606 throw new ExecutionException(ex);
\r
610 public Object get(long timeout, TimeUnit unit)
\r
611 throws InterruptedException, ExecutionException,
\r
613 executorSrv.shutdown();
\r
614 if (!executorSrv.isTerminated()) {
\r
615 executorSrv.awaitTermination(timeout, unit);
\r
626 * 読み取りに失敗した場合はエラーハンドラに通知されるが例外は返されない.<br>
\r
627 * 一つも正常なプロファイルがない場合は空のリストが返される.<br>
\r
628 * エラーハンドラの通知は非同期に行われる.
\r
630 * @param errorHandler
\r
631 * エラーハンドラ、不要ならばnull
\r
632 * @return プロファイルのリスト(表示名順)、もしくは空
\r
634 public List<CharacterData> listProfiles(
\r
635 final ProfileListErrorHandler errorHandler) {
\r
637 final List<CharacterData> profiles = new ArrayList<CharacterData>();
\r
639 Future<?> awaiter = listProfileAsync(new ListProfileCallback() {
\r
641 public boolean receiveCharacterData(CharacterData characterData) {
\r
642 synchronized (profiles) {
\r
643 profiles.add(characterData);
\r
648 public boolean occureException(File dir, Exception ex) {
\r
649 if (errorHandler != null) {
\r
650 errorHandler.occureException(dir, ex);
\r
656 // すべてのキャラクターデータが読み込まれるまで待機する.
\r
660 } catch (Exception ex) {
\r
661 logger.log(Level.WARNING, "listProfile abort.", ex);
\r
664 Collections.sort(profiles, CharacterData.SORT_DISPLAYNAME);
\r
666 return Collections.unmodifiableList(profiles);
\r
669 public CharacterData loadProfile(URI docBase) throws IOException {
\r
670 if (docBase == null) {
\r
671 throw new IllegalArgumentException();
\r
675 CharacterData characterData = characterDataXmlReader
\r
676 .loadCharacterDataFromXML(docBase);
\r
678 return characterData;
\r
683 * XMLの最初の要素と、その要素の属性にあるversionとxmlnsを取得して返す.<br>
\r
684 * XMLとして不正などの理由で取得できない場合はnullを返す.<br>
\r
685 * 読み込みそのものに失敗した場合は例外を返す.<br>
\r
688 * XMLコンテンツと想定される入力ストリーム
\r
689 * @return DocInfo情報、もしくはnull
\r
690 * @throws IOException
\r
693 public DocInfo readDocumentType(InputStream is) throws IOException {
\r
695 SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
\r
696 SAXParser saxParser;
\r
698 saxParser = saxParserFactory.newSAXParser();
\r
699 } catch (ParserConfigurationException ex) {
\r
700 throw new RuntimeException("JAXP Configuration Exception.", ex);
\r
701 } catch (SAXException ex) {
\r
702 throw new RuntimeException("JAXP Configuration Exception.", ex);
\r
706 final DocInfo[] result = new DocInfo[1];
\r
707 saxParser.parse(is, new DefaultHandler() {
\r
708 private int elmCount = 0;
\r
710 public void startElement(String uri, String localName,
\r
711 String qName, Attributes attributes)
\r
712 throws SAXException {
\r
713 if (elmCount == 0) {
\r
714 String version = attributes.getValue("version");
\r
715 String namespace = attributes.getValue("xmlns");
\r
716 DocInfo docInfo = new DocInfo();
\r
717 docInfo.setFirstElementName(qName);
\r
718 docInfo.setVersion(version);
\r
719 docInfo.setNamespace(namespace);
\r
720 result[0] = docInfo;
\r
727 } catch (SAXException ex) {
\r
728 logger.log(Level.INFO, "character.xml check failed.", ex);
\r
734 protected void saveCharacterDataToXML(CharacterData characterData)
\r
735 throws IOException {
\r
736 if (characterData == null) {
\r
737 throw new IllegalArgumentException();
\r
740 characterData.checkWritable();
\r
741 if (!characterData.isValid()) {
\r
742 throw new IOException("invalid profile: " + characterData);
\r
745 URI docBase = characterData.getDocBase();
\r
746 if (!"file".equals(docBase.getScheme())) {
\r
747 throw new IOException("ファイル以外はサポートしていません: " + docBase);
\r
751 ByteArrayOutputStream bos = new ByteArrayOutputStream();
\r
753 characterDataXmlWriter.writeXMLCharacterData(characterData, bos);
\r
759 File characterPropXML = new File(docBase);
\r
760 File baseDir = characterPropXML.getParentFile();
\r
761 if (!baseDir.exists()) {
\r
762 if (!baseDir.mkdirs()) {
\r
763 logger.log(Level.WARNING, "can't create directory. " + baseDir);
\r
767 FileOutputStream fos = new FileOutputStream(characterPropXML);
\r
769 fos.write(bos.toByteArray());
\r
775 public void saveFavorites(CharacterData characterData) throws IOException {
\r
776 if (characterData == null) {
\r
777 throw new IllegalArgumentException();
\r
781 UserData favoritesData = getFavoritesUserData(characterData);
\r
782 OutputStream os = favoritesData.getOutputStream();
\r
784 saveFavorites(characterData, os);
\r
791 private UserData getFavoritesUserData(CharacterData characterData) {
\r
792 if (characterData == null) {
\r
793 throw new IllegalArgumentException();
\r
796 // xml形式の場合、キャラクターディレクトリ上に設定する.
\r
797 URI docBase = characterData.getDocBase();
\r
798 File characterDir = new File(docBase).getParentFile();
\r
799 return new FileUserData(new File(characterDir, "favorites.xml"));
\r
802 protected void saveFavorites(CharacterData characterData,
\r
803 OutputStream outstm) throws IOException {
\r
804 if (characterData == null || outstm == null) {
\r
805 throw new IllegalArgumentException();
\r
810 DocumentBuilderFactory factory = DocumentBuilderFactory
\r
812 factory.setNamespaceAware(true);
\r
813 DocumentBuilder builder = factory.newDocumentBuilder();
\r
814 doc = builder.newDocument();
\r
816 } catch (ParserConfigurationException ex) {
\r
817 throw new RuntimeException("JAXP Configuration Exception.", ex);
\r
820 Element root = doc.createElementNS(NS, "partssets");
\r
822 root.setAttribute("xmlns:xml", XMLConstants.XML_NS_URI);
\r
823 root.setAttribute("xmlns:xsi",
\r
824 "http://www.w3.org/2001/XMLSchema-instance");
\r
825 root.setAttribute("xsi:schemaLocation", NS + " partsset.xsd");
\r
826 doc.appendChild(root);
\r
828 // presetsのelementを構築する.(Presetは除く)
\r
829 characterDataXmlWriter.writePartsSetElements(doc, root, characterData,
\r
833 TransformerFactory txFactory = TransformerFactory.newInstance();
\r
834 txFactory.setAttribute("indent-number", Integer.valueOf(4));
\r
837 tfmr = txFactory.newTransformer();
\r
838 } catch (TransformerConfigurationException ex) {
\r
839 throw new RuntimeException("JAXP Configuration Failed.", ex);
\r
841 tfmr.setOutputProperty(OutputKeys.INDENT, "yes");
\r
843 // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4504745
\r
844 final String encoding = "UTF-8";
\r
845 tfmr.setOutputProperty("encoding", encoding);
\r
847 tfmr.transform(new DOMSource(doc), new StreamResult(
\r
848 new OutputStreamWriter(outstm, Charset.forName(encoding))));
\r
850 } catch (TransformerException ex) {
\r
851 IOException ex2 = new IOException("XML Convert failed.");
\r
859 * お気に入り(Favorites)を読み込む.<br>
\r
860 * 現在のパーツセットに追加する形で読み込まれ、同じパーツセットIDのものは上書きされます.<br>
\r
862 * @param characterData
\r
864 * @throws IOException
\r
867 public void loadFavorites(CharacterData characterData) throws IOException {
\r
868 if (characterData == null) {
\r
869 throw new IllegalArgumentException();
\r
872 UserData favoritesXml = getFavoritesUserData(characterData);
\r
873 if (favoritesXml.exists()) {
\r
874 InputStream is = favoritesXml.openStream();
\r
876 characterDataXmlReader.loadPartsSet(characterData, is);
\r
886 * 既存のキャラクター定義を削除する.<br>
\r
887 * 有効なdocBaseがあり、そのxmlファイルが存在するものについて、削除を行う.<br>
\r
888 * forceRemoveがtrueでない場合はキャラクター定義 character.xmlファイルの拡張子を
\r
889 * リネームすることでキャラクター定義として認識させなくする.<br>
\r
890 * forceRevmoeがtrueの場合は実際にファイルを削除する.<br>
\r
891 * character.xml、favorites、workingsetのキャッシュも削除される.<br>
\r
895 * @param forceRemove
\r
896 * ファイルを削除する場合はtrue、リネームして無効にするだけならfalse
\r
897 * @throws IOException
\r
898 * 削除またはリネームできなかった場合
\r
900 public void remove(CharacterData cd, boolean forceRemove)
\r
901 throws IOException {
\r
902 if (cd == null || cd.getDocBase() == null) {
\r
903 throw new IllegalArgumentException();
\r
906 URI docBase = cd.getDocBase();
\r
907 File xmlFile = new File(docBase);
\r
908 if (!xmlFile.exists() || !xmlFile.isFile()) {
\r
915 UserData[] favoritesDatas = new UserData[]{getFavoritesUserData(cd)};
\r
916 for (UserData favoriteData : favoritesDatas) {
\r
917 if (favoriteData != null && favoriteData.exists()) {
\r
918 logger.log(Level.INFO, "remove file: " + favoriteData);
\r
919 favoriteData.delete();
\r
925 UserData workingSetSer = MainFrame.getWorkingSetUserData(cd, true);
\r
926 if (workingSetSer != null && workingSetSer.exists()) {
\r
927 logger.log(Level.INFO, "remove file: " + workingSetSer);
\r
928 workingSetSer.delete();
\r
931 // xmlファイルの拡張子を変更することでキャラクター定義として認識させない.
\r
932 // (削除に失敗するケースに備えて先にリネームする.)
\r
933 String suffix = "." + System.currentTimeMillis() + ".deleted";
\r
934 File bakFile = new File(xmlFile.getPath() + suffix);
\r
935 if (!xmlFile.renameTo(bakFile)) {
\r
936 throw new IOException("can not rename configuration file.:"
\r
941 File baseDir = xmlFile.getParentFile();
\r
943 if (!forceRemove) {
\r
944 // 削除されたディレクトリであることを識別できるようにディレクトリ名も変更する.
\r
945 File parentBak = new File(baseDir.getPath() + suffix);
\r
946 if (!baseDir.renameTo(parentBak)) {
\r
947 throw new IOException("can't rename directory. " + baseDir);
\r
952 removeRecursive(baseDir);
\r
957 * 指定したファイルを削除します.<br>
\r
958 * 指定したファイルがディレクトリを示す場合、このディレクトリを含む配下のすべてのファイルとディレクトリを削除します.<br>
\r
962 * @throws IOException
\r
965 protected void removeRecursive(File file) throws IOException {
\r
966 if (file == null) {
\r
967 throw new IllegalArgumentException();
\r
969 if (!file.exists()) {
\r
972 if (file.isDirectory()) {
\r
973 for (File child : file.listFiles()) {
\r
974 removeRecursive(child);
\r
977 if (!file.delete()) {
\r
978 throw new IOException("can't delete file. " + file);
\r
982 protected Iterable<Node> iterable(final NodeList nodeList) {
\r
984 if (nodeList == null) {
\r
987 mx = nodeList.getLength();
\r
989 return new Iterable<Node>() {
\r
990 public Iterator<Node> iterator() {
\r
991 return new Iterator<Node>() {
\r
992 private int idx = 0;
\r
993 public boolean hasNext() {
\r
996 public Node next() {
\r
998 throw new NoSuchElementException();
\r
1000 return nodeList.item(idx++);
\r
1002 public void remove() {
\r
1003 throw new UnsupportedOperationException();
\r
1010 protected Schema loadSchema(DocInfo docInfo) throws IOException {
\r
1011 if (docInfo == null) {
\r
1012 throw new IllegalArgumentException();
\r
1015 String schemaName = null;
\r
1016 if ("character".equals(docInfo.getFirstElementName())) {
\r
1017 if ("http://com.exmaple/charactermanaj".equals(docInfo
\r
1018 .getNamespace())) {
\r
1019 schemaName = CHARACTER_XML_SCHEMA_0_8;
\r
1020 } else if ("http://charactermanaj.sourceforge.jp/schema/charactermanaj"
\r
1021 .equals(docInfo.getNamespace())) {
\r
1022 schemaName = CHARACTER_XML_SCHEMA;
\r
1024 } else if ("partssets".equals(docInfo.getFirstElementName())) {
\r
1025 if ("http://com.exmaple/charactermanaj".equals(docInfo
\r
1026 .getNamespace())) {
\r
1027 schemaName = PARTSSET_XML_SCHEMA_0_8;
\r
1028 } else if ("http://charactermanaj.sourceforge.jp/schema/charactermanaj"
\r
1029 .equals(docInfo.getNamespace())) {
\r
1030 schemaName = PARTSSET_XML_SCHEMA;
\r
1033 if (schemaName == null) {
\r
1034 throw new IOException("unsupported namespace: " + docInfo);
\r
1037 Schema schema = schemaMap.get(schemaName);
\r
1038 if (schema != null) {
\r
1042 URL schemaURL = null;
\r
1044 SchemaFactory schemaFactory = SchemaFactory
\r
1045 .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
\r
1046 schemaFactory.setErrorHandler(errorHandler);
\r
1047 schemaURL = getEmbeddedResourceURL(schemaName);
\r
1048 schema = schemaFactory.newSchema(schemaURL);
\r
1049 schemaMap.put(schemaName, schema);
\r
1052 } catch (Exception ex) {
\r
1053 throw new RuntimeException("schema creation failed. :" + schemaURL,
\r
1058 protected URL getEmbeddedResourceURL(String schemaName) {
\r
1059 return this.getClass().getResource(schemaName);
\r
1062 protected XPath createXPath(DocInfo docInfo) {
\r
1063 if (docInfo == null) {
\r
1064 throw new IllegalArgumentException();
\r
1067 final String namespace;
\r
1068 if (docInfo.getNamespace() != null
\r
1069 && docInfo.getNamespace().length() > 0) {
\r
1070 namespace = docInfo.getNamespace();
\r
1075 XPathFactory xpathFactory = XPathFactory.newInstance();
\r
1076 XPath xpath = xpathFactory.newXPath();
\r
1077 xpath.setNamespaceContext(new NamespaceContext() {
\r
1078 public String getNamespaceURI(String prefix) {
\r
1079 if (prefix == null) {
\r
1080 throw new IllegalArgumentException();
\r
1082 if (prefix.equals("pre")) {
\r
1085 if (prefix.equals("xml")) {
\r
1086 return XMLConstants.XML_NS_URI;
\r
1088 return XMLConstants.NULL_NS_URI;
\r
1090 public Iterator<?> getPrefixes(String namespaceURI) {
\r
1091 throw new UnsupportedOperationException();
\r
1094 public String getPrefix(String namespaceURI) {
\r
1095 throw new UnsupportedOperationException();
\r
1102 * サンプルピクチャを読み込む.<br>
\r
1103 * ピクチャが存在しなければnullを返す. キャラクター定義がValidでない場合は常にnullを返す.<br>
\r
1105 * @param characterData
\r
1108 * イメージのローダー、null不可
\r
1109 * @return ピクチャのイメージ、もしくはnull
\r
1110 * @throws IOException
\r
1111 * ピクチャの読み取りに失敗した場合
\r
1113 public BufferedImage loadSamplePicture(CharacterData characterData,
\r
1114 ImageLoader loader) throws IOException {
\r
1115 if (characterData == null || loader == null) {
\r
1116 throw new IllegalArgumentException();
\r
1118 if (!characterData.isValid()) {
\r
1122 File sampleImageFile = getSamplePictureFile(characterData);
\r
1123 if (sampleImageFile != null && sampleImageFile.exists()) {
\r
1124 LoadedImage loadedImage = loader.load(new FileImageResource(
\r
1125 sampleImageFile));
\r
1126 return loadedImage.getImage();
\r
1132 * キャラクターのサンプルピクチャが登録可能であるか?<br>
\r
1133 * キャラクターデータが有効であり、且つ、ファイルの書き込みが可能であればtrueを返す.<br>
\r
1134 * キャラクターデータがnullもしくは無効であるか、ファイルプロトコルでないか、ファイルが書き込み禁止であればfalseょ返す.<br>
\r
1136 * @param characterData
\r
1138 * @return 書き込み可能であればtrue、そうでなければfalse
\r
1140 public boolean canSaveSamplePicture(CharacterData characterData) {
\r
1141 if (characterData == null || !characterData.isValid()) {
\r
1144 File sampleImageFile = getSamplePictureFile(characterData);
\r
1145 if (sampleImageFile != null) {
\r
1146 if (sampleImageFile.exists() && sampleImageFile.canWrite()) {
\r
1149 if (!sampleImageFile.exists()) {
\r
1150 File parentDir = sampleImageFile.getParentFile();
\r
1151 if (parentDir != null) {
\r
1152 return parentDir.canWrite();
\r
1160 * サンプルピクチャとして認識されるファイル位置を返す.<br>
\r
1161 * ファイルが実在するかは問わない.<br>
\r
1162 * DocBaseが未設定であるか、ファィルプロトコルとして返せない場合はnullを返す.<br>
\r
1164 * @param characterData
\r
1166 * @return サンプルピクチャの保存先のファイル位置、もしくはnull
\r
1168 protected File getSamplePictureFile(CharacterData characterData) {
\r
1169 if (characterData == null) {
\r
1170 throw new IllegalArgumentException();
\r
1172 URI docBase = characterData.getDocBase();
\r
1173 if (docBase != null && "file".endsWith(docBase.getScheme())) {
\r
1174 File docBaseFile = new File(docBase);
\r
1175 return new File(docBaseFile.getParentFile(), SAMPLE_IMAGE_FILENAME);
\r
1183 * @param characterData
\r
1185 * @param samplePicture
\r
1187 * @throws IOException
\r
1190 public void saveSamplePicture(CharacterData characterData,
\r
1191 BufferedImage samplePicture) throws IOException {
\r
1192 if (!canSaveSamplePicture(characterData)) {
\r
1193 throw new IOException("can not write a sample picture.:"
\r
1196 File sampleImageFile = getSamplePictureFile(characterData); // canSaveSamplePictureで書き込み先検証済み
\r
1198 if (samplePicture != null) {
\r
1201 // pngで保存するので背景色は透過になるが、一応、コードとしては入れておく。
\r
1202 AppConfig appConfig = AppConfig.getInstance();
\r
1203 Color sampleImageBgColor = appConfig.getSampleImageBgColor();
\r
1205 ImageSaveHelper imageSaveHelper = new ImageSaveHelper();
\r
1206 imageSaveHelper.savePicture(samplePicture, sampleImageBgColor,
\r
1207 sampleImageFile, null);
\r
1211 if (sampleImageFile.exists()) {
\r
1212 if (!sampleImageFile.delete()) {
\r
1213 throw new IOException("sample pucture delete failed. :"
\r
1214 + sampleImageFile);
\r
1221 * パーツ管理情報をDocBaseと同じフォルダ上のparts-info.xmlに書き出す.<br>
\r
1222 * XML生成中に失敗した場合は既存の管理情報は残される.<br>
\r
1223 * (管理情報の書き込み中にI/O例外が発生した場合は管理情報は破壊される.)<br>
\r
1226 * character.xmlの位置
\r
1227 * @param partsManageData
\r
1229 * @throws IOException
\r
1232 public void savePartsManageData(URI docBase, PartsManageData partsManageData)
\r
1233 throws IOException {
\r
1234 if (docBase == null || partsManageData == null) {
\r
1235 throw new IllegalArgumentException();
\r
1238 if (!"file".equals(docBase.getScheme())) {
\r
1239 throw new IOException("ファイル以外はサポートしていません: " + docBase);
\r
1241 File docBaseFile = new File(docBase);
\r
1242 File baseDir = docBaseFile.getParentFile();
\r
1244 // データからXMLを構築してストリームに出力する.
\r
1245 // 完全に成功したXMLのみ書き込むようにするため、一旦バッファする。
\r
1246 ByteArrayOutputStream bos = new ByteArrayOutputStream();
\r
1248 savePartsManageData(partsManageData, bos);
\r
1253 // バッファされたXMLデータを実際のファイルに書き込む
\r
1254 File partsInfoXML = new File(baseDir, "parts-info.xml");
\r
1255 FileOutputStream os = new FileOutputStream(partsInfoXML);
\r
1257 os.write(bos.toByteArray());
\r
1264 * パーツ管理情報をXMLとしてストリームに書き出す.<br>
\r
1266 * @param partsManageData
\r
1270 * @throws IOException
\r
1273 public void savePartsManageData(PartsManageData partsManageData,
\r
1274 OutputStream outstm) throws IOException {
\r
1275 if (partsManageData == null || outstm == null) {
\r
1276 throw new IllegalArgumentException();
\r
1281 DocumentBuilderFactory factory = DocumentBuilderFactory
\r
1283 factory.setNamespaceAware(true);
\r
1284 DocumentBuilder builder = factory.newDocumentBuilder();
\r
1285 doc = builder.newDocument();
\r
1287 } catch (ParserConfigurationException ex) {
\r
1288 throw new RuntimeException("JAXP Configuration Exception.", ex);
\r
1291 Locale locale = Locale.getDefault();
\r
1292 String lang = locale.getLanguage();
\r
1294 Element root = doc.createElementNS(NS_PARTSDEF, "parts-definition");
\r
1296 root.setAttribute("xmlns:xml", XMLConstants.XML_NS_URI);
\r
1297 root.setAttribute("xmlns:xsi",
\r
1298 "http://www.w3.org/2001/XMLSchema-instance");
\r
1299 root.setAttribute("xsi:schemaLocation", NS_PARTSDEF
\r
1300 + " parts-definition.xsd");
\r
1301 doc.appendChild(root);
\r
1304 Collection<PartsAuthorInfo> partsAuthors = partsManageData
\r
1305 .getAuthorInfos();
\r
1306 for (PartsAuthorInfo partsAuthorInfo : partsAuthors) {
\r
1307 String author = partsAuthorInfo.getAuthor();
\r
1308 if (author == null || author.length() == 0) {
\r
1313 Element nodeAuthor = doc.createElementNS(NS_PARTSDEF, "author");
\r
1314 Element nodeAuthorName = doc.createElementNS(NS_PARTSDEF, "name");
\r
1315 Attr attrLang = doc.createAttributeNS(XMLConstants.XML_NS_URI,
\r
1317 attrLang.setValue(lang);
\r
1318 nodeAuthorName.setAttributeNodeNS(attrLang);
\r
1319 nodeAuthorName.setTextContent(author);
\r
1320 nodeAuthor.appendChild(nodeAuthorName);
\r
1322 String homepageURL = partsAuthorInfo.getHomePage();
\r
1323 if (homepageURL != null && homepageURL.length() > 0) {
\r
1324 Element nodeHomepage = doc.createElementNS(NS_PARTSDEF,
\r
1326 Attr attrHomepageLang = doc.createAttributeNS(
\r
1327 XMLConstants.XML_NS_URI, "lang");
\r
1328 attrHomepageLang.setValue(lang);
\r
1329 nodeHomepage.setAttributeNodeNS(attrHomepageLang);
\r
1330 nodeHomepage.setTextContent(homepageURL);
\r
1331 nodeAuthor.appendChild(nodeHomepage);
\r
1334 root.appendChild(nodeAuthor);
\r
1336 Collection<PartsKey> partsKeys = partsManageData
\r
1337 .getPartsKeysByAuthor(author);
\r
1339 // ダウンロード別にパーツキーの集約
\r
1340 HashMap<String, List<PartsKey>> downloadMap = new HashMap<String, List<PartsKey>>();
\r
1341 for (PartsKey partsKey : partsKeys) {
\r
1342 PartsManageData.PartsVersionInfo versionInfo = partsManageData
\r
1343 .getVersionStrict(partsKey);
\r
1344 String downloadURL = versionInfo.getDownloadURL();
\r
1345 if (downloadURL == null) {
\r
1348 List<PartsKey> partsKeyGrp = downloadMap.get(downloadURL);
\r
1349 if (partsKeyGrp == null) {
\r
1350 partsKeyGrp = new ArrayList<PartsKey>();
\r
1351 downloadMap.put(downloadURL, partsKeyGrp);
\r
1353 partsKeyGrp.add(partsKey);
\r
1356 // ダウンロード別にパーツ情報の登録
\r
1357 ArrayList<String> downloadURLs = new ArrayList<String>(
\r
1358 downloadMap.keySet());
\r
1359 Collections.sort(downloadURLs);
\r
1361 for (String downloadURL : downloadURLs) {
\r
1362 List<PartsKey> partsKeyGrp = downloadMap.get(downloadURL);
\r
1363 Collections.sort(partsKeyGrp);
\r
1365 Element nodeDownload = doc.createElementNS(NS_PARTSDEF,
\r
1367 nodeDownload.setTextContent(downloadURL);
\r
1368 root.appendChild(nodeDownload);
\r
1370 for (PartsKey partsKey : partsKeyGrp) {
\r
1371 PartsManageData.PartsVersionInfo versionInfo = partsManageData
\r
1372 .getVersionStrict(partsKey);
\r
1374 Element nodeParts = doc.createElementNS(NS_PARTSDEF,
\r
1377 nodeParts.setAttribute("name", partsKey.getPartsName());
\r
1378 if (partsKey.getCategoryId() != null) {
\r
1379 nodeParts.setAttribute("category",
\r
1380 partsKey.getCategoryId());
\r
1382 if (versionInfo.getVersion() > 0) {
\r
1383 nodeParts.setAttribute("version",
\r
1384 Double.toString(versionInfo.getVersion()));
\r
1387 String localizedName = partsManageData
\r
1388 .getLocalizedName(partsKey);
\r
1389 if (localizedName != null
\r
1390 && localizedName.trim().length() > 0) {
\r
1391 Element nodeLocalizedName = doc.createElementNS(
\r
1392 NS_PARTSDEF, "local-name");
\r
1393 Attr attrLocalizedNameLang = doc.createAttributeNS(
\r
1394 XMLConstants.XML_NS_URI, "lang");
\r
1395 attrLocalizedNameLang.setValue(lang);
\r
1397 .setAttributeNodeNS(attrLocalizedNameLang);
\r
1398 nodeLocalizedName.setTextContent(localizedName);
\r
1399 nodeParts.appendChild(nodeLocalizedName);
\r
1402 root.appendChild(nodeParts);
\r
1408 TransformerFactory txFactory = TransformerFactory.newInstance();
\r
1409 txFactory.setAttribute("indent-number", Integer.valueOf(4));
\r
1412 tfmr = txFactory.newTransformer();
\r
1413 } catch (TransformerConfigurationException ex) {
\r
1414 throw new RuntimeException("JAXP Configuration Failed.", ex);
\r
1416 tfmr.setOutputProperty(OutputKeys.INDENT, "yes");
\r
1418 // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4504745
\r
1419 final String encoding = "UTF-8";
\r
1420 tfmr.setOutputProperty("encoding", encoding);
\r
1422 tfmr.transform(new DOMSource(doc), new StreamResult(
\r
1423 new OutputStreamWriter(outstm, Charset.forName(encoding))));
\r
1425 } catch (TransformerException ex) {
\r
1426 IOException ex2 = new IOException("XML Convert failed.");
\r
1427 ex2.initCause(ex);
\r
1433 * 指定したDocBaseと同じフォルダにあるparts-info.xmlからパーツ管理情報を取得して返す.<br>
\r
1434 * ファイルが存在しない場合は空のインスタンスを返す.<br>
\r
1435 * 返されるインスタンスは編集可能です.<br>
\r
1438 * character.xmlの位置
\r
1439 * @return パーツ管理情報、存在しない場合は空のインスタンス
\r
1440 * @throws IOException
\r
1443 public PartsManageData loadPartsManageData(URI docBase) throws IOException {
\r
1444 if (docBase == null) {
\r
1445 throw new IllegalArgumentException();
\r
1447 if (!"file".equals(docBase.getScheme())) {
\r
1448 throw new IOException("ファイル以外はサポートしていません。:" + docBase);
\r
1450 File docBaseFile = new File(docBase);
\r
1451 File baseDir = docBaseFile.getParentFile();
\r
1454 final File partsInfoXML = new File(baseDir, "parts-info.xml");
\r
1455 if (!partsInfoXML.exists()) {
\r
1456 // ファイルが存在しなければ空を返す.
\r
1457 return new PartsManageData();
\r
1460 PartsManageData partsManageData;
\r
1461 InputStream is = new FileInputStream(partsInfoXML);
\r
1463 partsManageData = loadPartsManageData(is);
\r
1467 return partsManageData;
\r
1470 public PartsManageData loadPartsManageData(InputStream is)
\r
1471 throws IOException {
\r
1473 throw new IllegalArgumentException();
\r
1477 final PartsManageData partsManageData = new PartsManageData();
\r
1480 SAXParser saxParser;
\r
1482 SAXParserFactory saxPartserFactory = SAXParserFactory.newInstance();
\r
1483 saxPartserFactory.setNamespaceAware(true);
\r
1484 saxParser = saxPartserFactory.newSAXParser();
\r
1485 } catch (Exception ex) {
\r
1486 throw new RuntimeException("JAXP Configuration failed.", ex);
\r
1489 // デフォルトのロケールから言語を取得
\r
1490 final Locale locale = Locale.getDefault();
\r
1491 final String lang = locale.getLanguage();
\r
1495 final LinkedList<String> stack = new LinkedList<String>();
\r
1497 // DOMではなくSAXで読み流す.
\r
1498 saxParser.parse(is, new DefaultHandler() {
\r
1499 private StringBuilder buf = new StringBuilder();
\r
1501 private PartsAuthorInfo partsAuthorInfo;
\r
1503 private String authorName;
\r
1504 private String homepageURL;
\r
1505 private String authorNameLang;
\r
1506 private String homepageLang;
\r
1507 private String downloadURL;
\r
1509 private String partsLocalNameLang;
\r
1510 private String partsLocalName;
\r
1511 private String partsCategoryId;
\r
1512 private String partsName;
\r
1513 private double partsVersion;
\r
1516 public void startDocument() throws SAXException {
\r
1517 logger.log(Level.FINEST, "parts-info : start");
\r
1521 public void endDocument() throws SAXException {
\r
1522 logger.log(Level.FINEST, "parts-info : end");
\r
1526 public void characters(char[] ch, int start, int length)
\r
1527 throws SAXException {
\r
1528 buf.append(ch, start, length);
\r
1531 public void startElement(String uri, String localName,
\r
1532 String qName, Attributes attributes)
\r
1533 throws SAXException {
\r
1534 stack.addFirst(qName);
\r
1535 int mx = stack.size();
\r
1536 if (mx >= 2 && stack.get(1).equals("parts")) {
\r
1537 if ("local-name".equals(qName)) {
\r
1538 partsLocalNameLang = attributes.getValue(
\r
1539 XMLConstants.XML_NS_URI, "lang");
\r
1542 } else if (mx >= 2 && stack.get(1).equals("author")) {
\r
1543 if ("name".equals(qName)) {
\r
1544 authorNameLang = attributes.getValue(
\r
1545 XMLConstants.XML_NS_URI, "lang");
\r
1547 } else if ("home-page".equals(qName)) {
\r
1548 homepageLang = attributes.getValue(
\r
1549 XMLConstants.XML_NS_URI, "lang");
\r
1552 } else if ("author".equals(qName)) {
\r
1553 partsAuthorInfo = null;
\r
1554 authorName = null;
\r
1555 authorNameLang = null;
\r
1556 homepageURL = null;
\r
1557 homepageLang = null;
\r
1559 } else if ("download-url".equals(qName)) {
\r
1560 downloadURL = null;
\r
1562 } else if ("parts".equals(qName)) {
\r
1563 partsLocalName = null;
\r
1564 partsLocalNameLang = null;
\r
1565 partsCategoryId = attributes.getValue("category");
\r
1566 partsName = attributes.getValue("name");
\r
1567 String strVersion = attributes.getValue("version");
\r
1569 if (strVersion == null || strVersion.length() == 0) {
\r
1570 partsVersion = 0.;
\r
1573 partsVersion = Double.parseDouble(strVersion);
\r
1574 if (partsVersion < 0) {
\r
1579 } catch (Exception ex) {
\r
1580 logger.log(Level.INFO,
\r
1581 "parts-info.xml: invalid version."
\r
1587 buf = new StringBuilder();
\r
1590 public void endElement(String uri, String localName,
\r
1591 String qName) throws SAXException {
\r
1593 int mx = stack.size();
\r
1595 if (mx >= 2 && "parts".equals(stack.get(1))) {
\r
1596 if ("local-name".equals(qName)) {
\r
1597 if (partsLocalName == null
\r
1598 || lang.equals(partsLocalNameLang)) {
\r
1599 partsLocalName = buf.toString();
\r
1603 } else if (mx >= 2 && "author".equals(stack.get(1))) {
\r
1604 if ("name".equals(qName)) {
\r
1605 if (authorName == null
\r
1606 || lang.equals(authorNameLang)) {
\r
1607 authorName = buf.toString();
\r
1610 } else if ("home-page".equals(qName)) {
\r
1611 if (homepageURL == null
\r
1612 || lang.equals(homepageLang)) {
\r
1613 homepageURL = buf.toString();
\r
1617 } else if ("author".equals(qName)) {
\r
1618 logger.log(Level.FINE, "parts-info: author: "
\r
1619 + authorName + " /homepage:" + homepageURL);
\r
1620 if (authorName != null && authorName.length() > 0) {
\r
1621 partsAuthorInfo = new PartsAuthorInfo();
\r
1622 partsAuthorInfo.setAuthor(authorName);
\r
1623 partsAuthorInfo.setHomePage(homepageURL);
\r
1626 partsAuthorInfo = null;
\r
1629 } else if ("download-url".equals(qName)) {
\r
1630 downloadURL = buf.toString();
\r
1631 logger.log(Level.FINE, "parts-info: download-url: "
\r
1634 } else if ("parts".equals(qName)) {
\r
1635 if (logger.isLoggable(Level.FINE)) {
\r
1636 logger.log(Level.FINE,
\r
1637 "parts-info.xml: parts-name: " + partsName
\r
1638 + " /category: " + partsCategoryId
\r
1639 + " /parts-local-name: "
\r
1640 + partsLocalName + " /version:"
\r
1644 PartsManageData.PartsVersionInfo versionInfo = new PartsManageData.PartsVersionInfo();
\r
1645 versionInfo.setVersion(partsVersion);
\r
1646 versionInfo.setDownloadURL(downloadURL);
\r
1648 PartsManageData.PartsKey partsKey = new PartsManageData.PartsKey(
\r
1649 partsName, partsCategoryId);
\r
1651 partsManageData.putPartsInfo(partsKey, partsLocalName,
\r
1652 partsAuthorInfo, versionInfo);
\r
1655 stack.removeFirst();
\r
1659 } catch (SAXException ex) {
\r
1660 IOException ex2 = new IOException("parts-info.xml read failed.");
\r
1661 ex2.initCause(ex);
\r
1665 return partsManageData;
\r
1670 * character.iniを読み取り、character.xmlを生成します.<br>
\r
1671 * character.xmlのシリアライズされた中間ファイルも生成されます.<br>
\r
1672 * すでにcharacter.xmlがある場合は上書きされます.<br>
\r
1673 * 途中でエラーが発生した場合はcharacter.xmlは削除されます.<br>
\r
1675 * @param characterIniFile
\r
1676 * 読み取るcharatcer.iniファイル
\r
1677 * @param characterXmlFile
\r
1678 * 書き込まれるcharacter.xmlファイル
\r
1679 * @throws IOException
\r
1682 public void convertFromCharacterIni(File characterIniFile,
\r
1683 File characterXmlFile) throws IOException {
\r
1684 if (characterIniFile == null || characterXmlFile == null) {
\r
1685 throw new IllegalArgumentException();
\r
1688 // character.iniから、character.xmlの内容を構築する.
\r
1689 FileInputStream is = new FileInputStream(characterIniFile);
\r
1690 CharacterData characterData;
\r
1692 CharacterDataIniReader iniReader = new CharacterDataIniReader();
\r
1693 characterData = iniReader.readCharacterDataFromIni(is);
\r
1700 URI docBase = characterXmlFile.toURI();
\r
1701 characterData.setDocBase(docBase);
\r
1703 // character.xmlの書き込み
\r
1704 boolean succeeded = false;
\r
1706 FileOutputStream outstm = new FileOutputStream(characterXmlFile);
\r
1708 characterDataXmlWriter.writeXMLCharacterData(characterData,
\r
1718 // 途中で失敗した場合は生成ファイルを削除しておく.
\r
1720 if (characterXmlFile.exists()) {
\r
1721 characterXmlFile.delete();
\r
1724 } catch (Exception ex) {
\r
1725 logger.log(Level.WARNING, "ファイルの削除に失敗しました。:"
\r
1726 + characterXmlFile, ex);
\r
1733 * お勧めリンクリストが設定されていない場合(nullの場合)、デフォルトのお勧めリストを設定する.<br>
\r
1734 * すでに設定されている場合(空を含む)は何もしない.<br>
\r
1736 * @param characterData
\r
1739 public void compensateRecommendationList(CharacterData characterData) {
\r
1740 if (characterData == null) {
\r
1741 throw new IllegalArgumentException();
\r
1743 if (characterData.getRecommendationURLList() != null) {
\r
1747 CharacterDataDefaultProvider defProv = new CharacterDataDefaultProvider();
\r
1748 CharacterData defaultCd = defProv.createDefaultCharacterData();
\r
1749 characterData.setRecommendationURLList(defaultCd
\r
1750 .getRecommendationURLList());
\r