1 package charactermanaj.model.io;
\r
3 import static charactermanaj.util.XMLUtilities.*;
\r
5 import java.awt.Color;
\r
6 import java.awt.Dimension;
\r
7 import java.io.IOException;
\r
8 import java.io.InputStream;
\r
10 import java.net.URL;
\r
11 import java.util.ArrayList;
\r
12 import java.util.LinkedHashMap;
\r
13 import java.util.List;
\r
14 import java.util.Locale;
\r
15 import java.util.Map;
\r
16 import java.util.logging.Level;
\r
17 import java.util.logging.Logger;
\r
19 import org.w3c.dom.Document;
\r
20 import org.w3c.dom.Element;
\r
22 import charactermanaj.graphics.colormodel.ColorModels;
\r
23 import charactermanaj.graphics.filters.ColorConv;
\r
24 import charactermanaj.graphics.filters.ColorConvertParameter;
\r
25 import charactermanaj.model.CharacterData;
\r
26 import charactermanaj.model.ColorGroup;
\r
27 import charactermanaj.model.IndependentPartsColorInfo;
\r
28 import charactermanaj.model.IndependentPartsSetInfo;
\r
29 import charactermanaj.model.IndependentPartsSetInfoList;
\r
30 import charactermanaj.model.Layer;
\r
31 import charactermanaj.model.PartsCategory;
\r
32 import charactermanaj.model.PartsSet;
\r
33 import charactermanaj.model.RecommendationURL;
\r
34 import charactermanaj.util.XMLUtilities;
\r
37 * キャラクターデータを格納したXMLを読み込むためのクラス.
\r
41 public class CharacterDataXMLReader {
\r
44 * character.xmlのデフォルトの名前空間
\r
46 private static final String NS_PREFIX = "http://charactermanaj.sourceforge.jp/schema/charactermanaj";
\r
49 * favorites.xmlのデフォルトの名前空間.
\r
50 * (character.xmlの名前空間と別にすべきだったかもしれないが、v0.991まで、これでやってきたので、とりあえず、このままとする。)
\r
52 private static final String NS_PREFIX_FAVORITES = "http://charactermanaj.sourceforge.jp/schema/charactermanaj";
\r
57 private static final Logger logger = Logger
\r
58 .getLogger(CharacterDataXMLReader.class.getName());
\r
61 * キャラクター定義(プロファイル)をロードする.
\r
66 * @throws IOException
\r
68 public CharacterData loadCharacterDataFromXML(URI docBase)
\r
69 throws IOException {
\r
70 if (docBase == null) {
\r
71 throw new IllegalArgumentException();
\r
74 URL docBaseURL = docBase.toURL();
\r
76 InputStream is = docBaseURL.openStream();
\r
78 cd = loadCharacterDataFromXML(is, docBase);
\r
87 * XMLコンテンツに対する入力ストリームからキャラクターデータを取り出す.<br>
\r
88 * docbaseはXMLファイルの位置を示すものであり、XMLデータ中には含まれず、キャラクターデータのロード時にセットされる.<br>
\r
89 * そのため引数としてdocbaseを引き渡す.<br>
\r
90 * 読み取りは現在のデフォルトロケールで行われる.<br>
\r
95 * XMLファイルの位置を示すURI、nullの場合はnullが設定される。
\r
98 * @return 読み取られたプロファイル
\r
99 * @throws IOException
\r
102 public CharacterData loadCharacterDataFromXML(InputStream is, URI docBase)
\r
103 throws IOException {
\r
104 return loadCharacterDataFromXML(is, docBase, Locale.getDefault());
\r
108 * XMLコンテンツに対する入力ストリームからキャラクターデータを取り出す.<br>
\r
109 * docbaseはXMLファイルの位置を示すものであり、XMLデータ中には含まれず、キャラクターデータのロード時にセットされる.<br>
\r
110 * そのため引数としてdocbaseを引き渡す.<br>
\r
111 * 設定ファイル中の表示文字列にロケール指定がある場合、引数に指定したロケールに合致する言語の情報を取得する.<br>
\r
112 * 合致するものがなければ最初のものを使用する.<br>
\r
117 * XMLファイルの位置を示すURI、nullの場合はnullが設定される。
\r
122 * @return 読み取られたプロファイル
\r
123 * @throws IOException
\r
126 public CharacterData loadCharacterDataFromXML(InputStream is, URI docBase,
\r
127 Locale locale) throws IOException {
\r
128 if (is == null || locale == null) {
\r
129 throw new IllegalArgumentException();
\r
132 Document doc = XMLUtilities.loadDocument(is);
\r
134 CharacterData characterData = new CharacterData();
\r
135 characterData.setDocBase(docBase);
\r
138 Element docElm = doc.getDocumentElement();
\r
139 if (!"character".equals(docElm.getNodeName())) {
\r
140 throw new IOException("Invalid Format.");
\r
142 String ns = docElm.getNamespaceURI();
\r
143 if (ns == null || !ns.startsWith(NS_PREFIX)) {
\r
144 throw new IOException("unsupported xml format");
\r
147 String docVersion = docElm.getAttribute("version").trim();
\r
148 if (!"1.0".equals(docVersion)) {
\r
149 throw new IOException("unsupported version: " + docVersion);
\r
151 String characterId = docElm.getAttribute("id").trim();
\r
152 String characterRev = docElm.getAttribute("rev").trim();
\r
154 characterData.setId(characterId);
\r
155 characterData.setRev(characterRev);
\r
158 String lang = locale.getLanguage();
\r
161 String characterName = getLocalizedElementText(docElm, "name", lang);
\r
162 if (characterName == null) {
\r
163 characterName = "default";
\r
165 characterData.setName(characterName.trim());
\r
167 // information/author, information/description
\r
168 String author = null;
\r
169 String description = null;
\r
170 for (Element infoElm : getChildElements(docElm, "information")) {
\r
171 if (author == null) {
\r
172 author = getLocalizedElementText(infoElm, "author", lang);
\r
174 if (description == null) {
\r
175 description = getLocalizedElementText(infoElm,
\r
176 "description", lang);
\r
179 if (author == null) {
\r
182 characterData.setAuthor(author.trim());
\r
184 if (description == null) {
\r
185 description = null;
\r
187 characterData.setDescription(description);
\r
189 // image-size/width, image-size/height
\r
193 for (Element sizeElm : getChildElements(docElm, "image-size")) {
\r
194 String tmpWidth = getLocalizedElementText(sizeElm, "width",
\r
196 if (tmpWidth != null && tmpWidth.trim().length() > 0) {
\r
197 width = Integer.parseInt(tmpWidth.trim());
\r
199 String tmpHeight = getLocalizedElementText(sizeElm, "height",
\r
201 if (tmpHeight != null && tmpHeight.trim().length() > 0) {
\r
202 height = Integer.parseInt(tmpHeight.trim());
\r
212 characterData.setImageSize(new Dimension(width, height));
\r
215 for (Element settingElm : getChildElements(docElm, "settings")) {
\r
216 for (Element entElm : getChildElements(settingElm, "entry")) {
\r
217 String key = entElm.getAttribute("key").trim();
\r
218 String val = entElm.getTextContent();
\r
219 characterData.setProperty(key, val);
\r
223 // colorGroups/colorGroup
\r
224 ArrayList<ColorGroup> colorGroups = new ArrayList<ColorGroup>();
\r
225 for (Element colorGroupsElm : getChildElements(docElm,
\r
227 for (Element colorGroupElm : getChildElements(colorGroupsElm,
\r
229 String colorGroupId = colorGroupElm.getAttribute("id")
\r
231 String colorGroupDisplayName = getLocalizedElementText(
\r
232 colorGroupElm, "display-name", lang);
\r
234 ColorGroup colorGroup = new ColorGroup(colorGroupId,
\r
235 colorGroupDisplayName);
\r
236 colorGroups.add(colorGroup);
\r
239 characterData.setColorGroups(colorGroups);
\r
241 // categories/category
\r
242 ArrayList<PartsCategory> categories = new ArrayList<PartsCategory>();
\r
243 for (Element catsElm : getChildElements(docElm, "categories")) {
\r
244 for (Element catElm : getChildElements(catsElm, "category")) {
\r
245 String categoryId = catElm.getAttribute("id").trim();
\r
246 boolean multipleSelectable = Boolean.parseBoolean(catElm
\r
247 .getAttribute("multipleSelectable"));
\r
249 String categoryDisplayName = getLocalizedElementText(
\r
250 catElm, "display-name", lang);
\r
252 int visibleRows = 0;
\r
253 String tmpVisibleRows = getLocalizedElementText(catElm,
\r
254 "visible-rows", lang);
\r
255 if (tmpVisibleRows != null
\r
256 && tmpVisibleRows.trim().length() > 0) {
\r
257 visibleRows = Integer.parseInt(tmpVisibleRows.trim());
\r
259 if (visibleRows <= 0) {
\r
264 ArrayList<Layer> layers = new ArrayList<Layer>();
\r
265 for (Element layersElm : getChildElements(catElm, "layers")) {
\r
266 for (Element layerElm : getChildElements(layersElm,
\r
268 String layerId = layerElm.getAttribute("id");
\r
269 String layerDisplayName = getLocalizedElementText(
\r
270 layerElm, "display-name", lang);
\r
273 String strOrder = getElementText(layerElm, "order");
\r
274 int order = layers.size();
\r
275 if (strOrder != null
\r
276 && strOrder.trim().length() > 0) {
\r
277 order = Integer.parseInt(strOrder.trim());
\r
281 String layerDir = getElementText(layerElm, "dir");
\r
282 if (layerDir == null
\r
283 || layerDir.trim().length() == 0) {
\r
284 throw new IOException("layer's dir is null");
\r
288 String colorModelName = getElementText(layerElm,
\r
290 if (colorModelName == null
\r
291 || colorModelName.length() == 0) {
\r
292 // 省略時はデフォルトのカラーモデル名を使用する.
\r
293 colorModelName = ColorModels.DEFAULT.name();
\r
296 // layer/colorGroup カラーグループ
\r
297 boolean initSync = false;
\r
298 ColorGroup colorGroup = null;
\r
299 Element lcgElm = getFirstChildElement(layerElm,
\r
301 if (lcgElm != null) {
\r
302 String tmpInitSync = lcgElm
\r
303 .getAttribute("init-sync");
\r
304 if (tmpInitSync.trim().length() > 0) {
\r
305 initSync = Boolean.parseBoolean(tmpInitSync
\r
308 if (colorGroup == null) {
\r
309 String colorGroupRefId = lcgElm
\r
310 .getAttribute("refid").trim();
\r
311 colorGroup = characterData
\r
312 .getColorGroup(colorGroupRefId);
\r
316 Layer layer = new Layer(layerId, layerDisplayName,
\r
317 order, colorGroup, initSync, layerDir,
\r
323 PartsCategory category = new PartsCategory(
\r
324 categories.size(), categoryId, categoryDisplayName,
\r
325 multipleSelectable, visibleRows,
\r
326 layers.toArray(new Layer[layers.size()]));
\r
327 categories.add(category);
\r
330 characterData.setPartsCategories(categories
\r
331 .toArray(new PartsCategory[categories.size()]));
\r
334 for (Element presetssElm : getChildElements(docElm, "presets")) {
\r
335 loadPartsSet(characterData, presetssElm, true, lang);
\r
339 List<RecommendationURL> recommendationURLList = null; // お勧めノードがない場合はnull
\r
340 for (Element recmsElm : getChildElements(docElm, "recommendations")) {
\r
341 for (Element recmElm : getChildElements(recmsElm,
\r
342 "recommendation")) {
\r
343 String recommentDescription = getLocalizedElementText(
\r
344 recmElm, "description", lang);
\r
345 String url = getLocalizedElementText(recmElm, "URL", lang);
\r
347 if (recommentDescription != null) {
\r
348 recommentDescription = recommentDescription.trim();
\r
354 RecommendationURL recommendationURL = new RecommendationURL();
\r
355 recommendationURL.setDisplayName(recommentDescription);
\r
356 recommendationURL.setUrl(url);
\r
358 if (recommendationURLList == null) {
\r
359 recommendationURLList = new ArrayList<RecommendationURL>();
\r
361 recommendationURLList.add(recommendationURL);
\r
364 characterData.setRecommendationURLList(recommendationURLList);
\r
366 } catch (RuntimeException ex) {
\r
367 IOException ex2 = new IOException("CharacterData invalid format.");
\r
372 return characterData;
\r
376 * 入力ストリームからパーツセット定義(Favorites.xml)を読み込んで、 characterDataに追加登録する.<br>
\r
378 * @param characterData
\r
379 * お気に入りを登録されるキャラクターデータ
\r
381 * お気に入りのxmlへの入力ストリーム
\r
384 * @throws IOException
\r
387 public void loadPartsSet(CharacterData characterData, InputStream inpstm)
\r
388 throws IOException {
\r
389 if (characterData == null || inpstm == null) {
\r
390 throw new IllegalArgumentException();
\r
393 Document doc = XMLUtilities.loadDocument(inpstm);
\r
394 Element docElm = doc.getDocumentElement();
\r
395 if (!"partssets".equals(docElm.getNodeName())) {
\r
396 logger.log(Level.WARNING, "invalid partsets format.");
\r
400 String ns = docElm.getNamespaceURI();
\r
401 if (ns == null || !ns.startsWith(NS_PREFIX_FAVORITES)) {
\r
402 logger.log(Level.WARNING, "invalid partsets format.");
\r
406 String lang = Locale.getDefault().getLanguage();
\r
407 loadPartsSet(characterData, docElm, false, lang);
\r
411 * CharacterDataのプリセットまたはFavoritesのパーツセットのXMLからパーツセットを読み取って登録する.<br>
\r
413 * @param characterData
\r
415 * @param nodePartssets
\r
416 * パーツセットのノード、プリセットまたはパーツセットノード
\r
417 * @param presetParts
\r
418 * ロードしたパーツセットにプリセットフラグをたてる場合はtrue
\r
422 protected void loadPartsSet(CharacterData characterData,
\r
423 Element nodePartssets, boolean presetParts, String lang) {
\r
424 IndependentPartsSetInfoList partsSetLst = loadPartsSetList(
\r
425 nodePartssets, lang);
\r
426 logger.info("partsSetList: size=" + partsSetLst.size());
\r
430 .setDefaultPartsSetId(partsSetLst.getDefaultPresetId());
\r
433 for (IndependentPartsSetInfo partsSetInfo : partsSetLst) {
\r
434 PartsSet partsSet = IndependentPartsSetInfo.convertPartsSet(
\r
435 partsSetInfo, characterData, presetParts);
\r
436 characterData.addPartsSet(partsSet);
\r
441 * CharacterDataのプリセットまたはFavoritesのパーツセットのXMLからパーツセットを読み取って登録する.<br>
\r
443 * @param nodePartssets
\r
444 * パーツセットのノード、プリセットまたはパーツセットノード
\r
448 public IndependentPartsSetInfoList loadPartsSetList(Element nodePartssets,
\r
450 if (nodePartssets == null || lang == null || lang.length() == 0) {
\r
451 throw new IllegalArgumentException();
\r
454 IndependentPartsSetInfoList partsSetLst = new IndependentPartsSetInfoList();
\r
457 String defaultPresetId = nodePartssets.getAttribute("default-preset");
\r
458 if (defaultPresetId != null) {
\r
459 defaultPresetId = defaultPresetId.trim();
\r
463 for (Element presetElm : getChildElements(nodePartssets, "preset")) {
\r
464 IndependentPartsSetInfo partsSetInfo = loadPartsSet(presetElm, lang);
\r
465 if (partsSetInfo != null) {
\r
466 String partsSetId = partsSetInfo.getId();
\r
468 // デフォルトのパーツセットIDがない場合は先頭をデフォルトとみなす.
\r
469 if (defaultPresetId == null || defaultPresetId.length() == 0) {
\r
470 defaultPresetId = partsSetId;
\r
473 partsSetLst.add(partsSetInfo);
\r
477 if (defaultPresetId.length() == 0) {
\r
478 // デフォルトパーツセットがないことを示すためのnull
\r
479 defaultPresetId = null;
\r
481 partsSetLst.setDefaultPresetId(defaultPresetId);
\r
483 return partsSetLst;
\r
487 * CharacterDataのプリセットまたはFavoritesのパーツセットのXMLからパーツセットを読み取る.<br>
\r
489 * @param nodePartssets
\r
490 * パーツセットのノード、プリセットまたはパーツセットノード
\r
493 * @return 素のパーツセット情報、無ければnull
\r
495 public IndependentPartsSetInfo loadPartsSet(Element presetElm, String lang) {
\r
496 if (presetElm == null || lang == null) {
\r
500 IndependentPartsSetInfo partsSetInfo = new IndependentPartsSetInfo();
\r
503 String partsSetId = presetElm.getAttribute("id");
\r
504 if (partsSetId != null) {
\r
505 partsSetId = partsSetId.trim();
\r
507 if (partsSetId != null && partsSetId.length() == 0) {
\r
510 partsSetInfo.setId(partsSetId);
\r
513 String displayName = getLocalizedElementText(presetElm, "display-name",
\r
515 partsSetInfo.setDisplayName(displayName);
\r
518 Element bgColorElm = getFirstChildElement(presetElm, "background-color");
\r
519 if (bgColorElm != null) {
\r
520 String tmpBgColor = bgColorElm.getAttribute("color");
\r
522 Color bgColor = Color.decode(tmpBgColor);
\r
523 partsSetInfo.setBackgroundColor(bgColor);
\r
525 } catch (Exception ex) {
\r
526 logger.log(Level.WARNING, "bgColor parameter is invalid. :"
\r
532 // affine-transform-parameter
\r
533 String tmpAffienTrans = getElementText(presetElm,
\r
534 "affine-transform-parameter");
\r
535 if (tmpAffienTrans != null && tmpAffienTrans.trim().length() > 0) {
\r
537 ArrayList<Double> affineTransformParameterArr = new ArrayList<Double>();
\r
538 for (String strParam : tmpAffienTrans.split("\\s+")) {
\r
539 affineTransformParameterArr.add(Double.valueOf(strParam));
\r
541 double[] affineTransformParameter = new double[affineTransformParameterArr
\r
544 for (double aaffineItem : affineTransformParameterArr) {
\r
545 affineTransformParameter[idx++] = aaffineItem;
\r
548 .setAffineTransformParameter(affineTransformParameter);
\r
550 } catch (Exception ex) {
\r
551 logger.log(Level.WARNING,
\r
552 "affine transform parameter is invalid. :"
\r
553 + tmpAffienTrans, ex);
\r
558 // カテゴリIDをキーとし、パーツ名をキーとしカラー情報のリストを値とするマップを値とする.
\r
559 Map<String, Map<String, List<IndependentPartsColorInfo>>> partsMap = partsSetInfo
\r
563 for (Element catElm : getChildElements(presetElm, "category")) {
\r
564 String categoryId = catElm.getAttribute("refid");
\r
565 if (categoryId != null) {
\r
566 categoryId = categoryId.trim();
\r
568 if (categoryId == null || categoryId.length() == 0) {
\r
569 logger.log(Level.WARNING, "missing category refid: " + catElm);
\r
573 // パーツ名をキーとしカラー情報のリストを値とするマップ.
\r
574 Map<String, List<IndependentPartsColorInfo>> categoryPartsMap = partsMap
\r
576 if (categoryPartsMap == null) {
\r
577 // ※ パーツの出現順を維持する必要があるためLinkedHashMapを用いる
\r
578 categoryPartsMap = new LinkedHashMap<String, List<IndependentPartsColorInfo>>();
\r
579 partsMap.put(categoryId, categoryPartsMap);
\r
583 for (Element partsElm : getChildElements(catElm, "parts")) {
\r
584 String partsName = partsElm.getAttribute("name");
\r
585 if (partsName != null) {
\r
586 partsName = partsName.trim();
\r
588 if (partsName == null || partsName.length() == 0) {
\r
589 logger.log(Level.WARNING, "missing parts name. " + partsElm);
\r
594 List<IndependentPartsColorInfo> infoList = null;
\r
595 for (Element colorElm : getChildElements(partsElm, "color")) {
\r
596 infoList = readPartsColor(colorElm);
\r
599 categoryPartsMap.put(partsName, infoList);
\r
602 return partsSetInfo;
\r
606 * パーツごとのカラー情報のXMLを読み込んで返す.<br>
\r
607 * パーツは複数のレイヤーから構成されるので、複数レイヤーのカラー情報のリストとして返される.<br>
\r
608 * (パーツは複数レイヤーをまとめる1つのカテゴリになるので、カテゴリ単位の情報となる.)<br>
\r
614 public List<IndependentPartsColorInfo> readPartsColor(Element colorElm) {
\r
615 if (colorElm == null) {
\r
616 throw new IllegalArgumentException();
\r
619 ArrayList<IndependentPartsColorInfo> infoList = new ArrayList<IndependentPartsColorInfo>();
\r
620 for (Element layerElm : getChildElements(colorElm, "layer")) {
\r
621 IndependentPartsColorInfo info = new IndependentPartsColorInfo();
\r
623 String layerId = layerElm.getAttribute("refid");
\r
624 if (layerId != null) {
\r
625 layerId = layerId.trim();
\r
627 if (layerId == null || layerId.length() == 0) {
\r
628 logger.log(Level.WARNING, "missing layer-id: " + layerElm);
\r
631 info.setLayerId(layerId);
\r
634 Element colorGroupElm = getFirstChildElement(layerElm,
\r
636 if (colorGroupElm != null) {
\r
637 String colorGroupId = colorGroupElm.getAttribute("group")
\r
639 info.setColorGroupId(colorGroupId);
\r
640 boolean syncColorGroup = Boolean.parseBoolean(colorGroupElm
\r
641 .getAttribute("synchronized").trim());
\r
642 info.setSyncColorGroup(syncColorGroup);
\r
646 ColorConvertParameter param = info.getColorConvertParameter();
\r
647 Element nodeRgb = getFirstChildElement(layerElm, "rgb");
\r
648 if (nodeRgb != null) {
\r
649 for (Element elmRgb : getChildElements(nodeRgb, null)) {
\r
650 String rgbName = elmRgb.getNodeName();
\r
651 int offset = Integer
\r
652 .parseInt(elmRgb.getAttribute("offset"));
\r
653 float factor = Float.parseFloat(elmRgb
\r
654 .getAttribute("factor"));
\r
655 float gamma = Float
\r
656 .parseFloat(elmRgb.getAttribute("gamma"));
\r
657 if ("red".equals(rgbName)) {
\r
658 param.setOffsetR(offset);
\r
659 param.setFactorR(factor);
\r
660 param.setGammaR(gamma);
\r
661 } else if ("green".equals(rgbName)) {
\r
662 param.setOffsetG(offset);
\r
663 param.setFactorG(factor);
\r
664 param.setGammaG(gamma);
\r
665 } else if ("blue".equals(rgbName)) {
\r
666 param.setOffsetB(offset);
\r
667 param.setFactorB(factor);
\r
668 param.setGammaB(gamma);
\r
669 } else if ("alpha".equals(rgbName)) {
\r
670 param.setOffsetA(offset);
\r
671 param.setFactorA(factor);
\r
672 param.setGammaA(gamma);
\r
678 Element elmHsb = getFirstChildElement(layerElm, "hsb");
\r
679 if (elmHsb != null) {
\r
680 float hue = Float.parseFloat(elmHsb.getAttribute("hue"));
\r
681 float saturation = Float.parseFloat(elmHsb
\r
682 .getAttribute("saturation"));
\r
683 float brightness = Float.parseFloat(elmHsb
\r
684 .getAttribute("brightness"));
\r
685 String strContrast = elmHsb.getAttribute("contrast").trim();
\r
687 param.setSaturation(saturation);
\r
688 param.setBrightness(brightness);
\r
689 if (strContrast != null && strContrast.length() > 0) {
\r
690 // ver0.96追加 optional
\r
691 float contrast = Float.parseFloat(strContrast);
\r
692 param.setContrast(contrast);
\r
697 Element elmRgbReplace = getFirstChildElement(layerElm,
\r
699 if (elmRgbReplace != null) {
\r
700 Float grayLevel = Float.parseFloat(elmRgbReplace
\r
701 .getAttribute("gray"));
\r
702 ColorConv colorType = ColorConv.valueOf(elmRgbReplace
\r
703 .getAttribute("replace-type"));
\r
704 param.setGrayLevel(grayLevel);
\r
705 param.setColorReplace(colorType);
\r
708 infoList.add(info);
\r