1 package charactermanaj.model.io;
4 import java.io.IOException;
5 import java.io.OutputStream;
6 import java.io.OutputStreamWriter;
7 import java.nio.charset.Charset;
8 import java.util.Collection;
9 import java.util.HashMap;
10 import java.util.List;
11 import java.util.Locale;
15 import javax.xml.XMLConstants;
16 import javax.xml.parsers.DocumentBuilder;
17 import javax.xml.parsers.DocumentBuilderFactory;
18 import javax.xml.parsers.ParserConfigurationException;
19 import javax.xml.transform.OutputKeys;
20 import javax.xml.transform.Transformer;
21 import javax.xml.transform.TransformerConfigurationException;
22 import javax.xml.transform.TransformerException;
23 import javax.xml.transform.TransformerFactory;
24 import javax.xml.transform.dom.DOMSource;
25 import javax.xml.transform.stream.StreamResult;
27 import org.w3c.dom.Attr;
28 import org.w3c.dom.Document;
29 import org.w3c.dom.Element;
31 import charactermanaj.graphics.filters.ColorConv;
32 import charactermanaj.graphics.filters.ColorConvertParameter;
33 import charactermanaj.model.CharacterData;
34 import charactermanaj.model.ColorGroup;
35 import charactermanaj.model.ColorInfo;
36 import charactermanaj.model.Layer;
37 import charactermanaj.model.PartsCategory;
38 import charactermanaj.model.PartsColorInfo;
39 import charactermanaj.model.PartsIdentifier;
40 import charactermanaj.model.PartsSet;
41 import charactermanaj.model.RecommendationURL;
44 * パーツ管理情報のXMLへの書き込み用クラス.
48 public class CharacterDataXMLWriter {
53 private static final String VERSION_SIG_1_0 = "1.0";
56 * キャラクター定義XMLファイルの名前空間
58 private static final String DEFAULT_NS = "http://charactermanaj.sourceforge.jp/schema/charactermanaj";
60 private final String NS;
62 public CharacterDataXMLWriter() {
66 public CharacterDataXMLWriter(String ns) {
73 * @param characterData
77 public void writeXMLCharacterData(CharacterData characterData,
78 OutputStream outstm) throws IOException {
79 if (outstm == null || characterData == null) {
80 throw new IllegalArgumentException();
83 Locale locale = Locale.getDefault();
84 String lang = locale.getLanguage();
88 DocumentBuilderFactory factory = DocumentBuilderFactory
90 factory.setNamespaceAware(true);
91 DocumentBuilder builder = factory.newDocumentBuilder();
92 doc = builder.newDocument();
93 } catch (ParserConfigurationException ex) {
94 throw new RuntimeException("JAXP Configuration failed.", ex);
97 Element root = doc.createElementNS(NS, "character");
98 root.setAttribute("version", VERSION_SIG_1_0);
100 root.setAttribute("xmlns:xml", XMLConstants.XML_NS_URI);
101 root.setAttribute("xmlns:xsi", XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI);
102 root.setAttribute("xsi:schemaLocation", NS + " character.xsd");
103 root.setAttribute("id", characterData.getId());
104 root.setAttribute("rev", characterData.getRev());
105 doc.appendChild(root);
108 Element nodeName = doc.createElementNS(NS, "name");
109 Attr attrLang = doc.createAttributeNS(XMLConstants.XML_NS_URI, "lang");
110 attrLang.setValue(lang);
111 nodeName.setAttributeNodeNS(attrLang);
112 nodeName.setTextContent(characterData.getName());
113 root.appendChild(nodeName);
116 String author = characterData.getAuthor();
117 String description = characterData.getDescription();
118 if ((author != null && author.length() > 0)
119 || (description != null && description.length() > 0)) {
120 Element nodeInfomation = doc.createElementNS(NS, "information");
121 if (author != null && author.length() > 0) {
122 Element nodeAuthor = doc.createElementNS(NS, "author");
123 Attr attrNodeAuthorLang = doc.createAttributeNS(
124 XMLConstants.XML_NS_URI, "lang");
125 attrNodeAuthorLang.setValue(lang);
126 nodeAuthor.setAttributeNodeNS(attrNodeAuthorLang);
127 nodeAuthor.setTextContent(author);
128 nodeInfomation.appendChild(nodeAuthor);
130 if (description != null && description.length() > 0) {
132 // 説明の改行コードはXML上ではLFとする.
133 description = description.replace("\r\n", "\n");
134 description = description.replace("\r", "\n");
136 Element nodeDescription = doc
137 .createElementNS(NS, "description");
138 Attr attrNodeDescriptionLang = doc.createAttributeNS(
139 XMLConstants.XML_NS_URI, "lang");
140 attrNodeDescriptionLang.setValue(lang);
141 nodeDescription.setAttributeNodeNS(attrNodeDescriptionLang);
142 nodeDescription.setTextContent(description);
143 nodeInfomation.appendChild(nodeDescription);
145 root.appendChild(nodeInfomation);
149 Element nodeSize = doc.createElementNS(NS, "image-size");
150 Element nodeWidth = doc.createElementNS(NS, "width");
151 nodeWidth.setTextContent(Integer.toString((int) characterData
152 .getImageSize().getWidth()));
153 Element nodeHeight = doc.createElementNS(NS, "height");
154 nodeHeight.setTextContent(Integer.toString((int) characterData
155 .getImageSize().getHeight()));
156 nodeSize.appendChild(nodeWidth);
157 nodeSize.appendChild(nodeHeight);
158 root.appendChild(nodeSize);
161 Element nodeSettings = doc.createElementNS(NS, "settings");
162 root.appendChild(nodeSettings);
163 for (String settingsEntryName : characterData.getPropertyNames()) {
164 String value = characterData.getProperty(settingsEntryName);
166 Element nodeEntry = doc.createElementNS(NS, "entry");
167 nodeEntry.setAttribute("key", settingsEntryName);
168 nodeEntry.setTextContent(value);
169 nodeSettings.appendChild(nodeEntry);
174 Element nodeCategories = doc.createElementNS(NS, "categories");
175 for (PartsCategory category : characterData.getPartsCategories()) {
177 Element nodeCategory = doc.createElementNS(NS, "category");
178 nodeCategory.setAttribute("id", category.getCategoryId());
180 nodeCategory.setAttribute("multipleSelectable",
181 category.isMultipleSelectable() ? "true" : "false");
182 nodeCategory.setAttribute("optional",
183 category.isOptional() ? "true" : "false");
186 Element nodeVisibleRows = doc.createElementNS(NS, "visible-rows");
187 nodeVisibleRows.setTextContent(Integer.toString(category
189 nodeCategory.appendChild(nodeVisibleRows);
192 Element nodeCategoryName = doc.createElementNS(NS, "display-name");
193 Attr attrCategoryNameLang = doc.createAttributeNS(
194 XMLConstants.XML_NS_URI, "lang");
195 attrCategoryNameLang.setValue(lang);
196 nodeCategoryName.setAttributeNodeNS(attrCategoryNameLang);
198 .setTextContent(category.getLocalizedCategoryName());
199 nodeCategory.appendChild(nodeCategoryName);
202 Element nodeLayers = doc.createElementNS(NS, "layers");
203 for (Layer layer : category.getLayers()) {
205 Element nodeLayer = doc.createElementNS(NS, "layer");
206 nodeLayer.setAttribute("id", layer.getId());
208 Element nodeLayerName = doc.createElementNS(NS, "display-name");
209 Attr attrLayerNameLang = doc.createAttributeNS(
210 XMLConstants.XML_NS_URI, "lang");
211 attrLayerNameLang.setValue(lang);
212 nodeLayerName.setAttributeNodeNS(attrLayerNameLang);
213 nodeLayerName.setTextContent(layer.getLocalizedName());
214 nodeLayer.appendChild(nodeLayerName);
216 Element nodeOrder = doc.createElementNS(NS, "order");
217 nodeOrder.setTextContent(Integer.toString(layer.getOrder()));
218 nodeLayer.appendChild(nodeOrder);
220 ColorGroup colorGroup = layer.getColorGroup();
221 if (colorGroup != null && colorGroup.isEnabled()) {
222 Element nodeColorGroup = doc.createElementNS(NS,
224 nodeColorGroup.setAttribute("refid", colorGroup.getId());
225 nodeColorGroup.setAttribute("init-sync", layer.isInitSync()
228 nodeLayer.appendChild(nodeColorGroup);
231 Element nodeDir = doc.createElementNS(NS, "dir");
232 nodeDir.setTextContent(layer.getDir());
233 nodeLayer.appendChild(nodeDir);
235 String colorModelName = layer.getColorModelName();
236 if (colorModelName != null && colorModelName.length() > 0) {
237 Element nodeColorModel = doc.createElementNS(NS,
239 nodeColorModel.setTextContent(layer.getColorModelName());
240 nodeLayer.appendChild(nodeColorModel);
243 nodeLayers.appendChild(nodeLayer);
245 nodeCategory.appendChild(nodeLayers);
247 nodeCategories.appendChild(nodeCategory);
249 root.appendChild(nodeCategories);
252 Collection<ColorGroup> colorGroups = characterData.getColorGroups();
253 if (colorGroups.size() > 0) {
254 Element nodeColorGroups = doc.createElementNS(NS, "colorGroups");
255 int colorGroupCount = 0;
256 for (ColorGroup colorGroup : colorGroups) {
257 if (!colorGroup.isEnabled()) {
260 Element nodeColorGroup = doc.createElementNS(NS, "colorGroup");
261 nodeColorGroup.setAttribute("id", colorGroup.getId());
262 Element nodeColorGroupName = doc.createElementNS(NS,
264 Attr attrColorGroupNameLang = doc.createAttributeNS(
265 XMLConstants.XML_NS_URI, "lang");
266 attrColorGroupNameLang.setValue(lang);
267 nodeColorGroupName.setAttributeNodeNS(attrColorGroupNameLang);
269 .setTextContent(colorGroup.getLocalizedName());
270 nodeColorGroup.appendChild(nodeColorGroupName);
271 nodeColorGroups.appendChild(nodeColorGroup);
274 if (colorGroupCount > 0) {
275 root.appendChild(nodeColorGroups);
280 List<RecommendationURL> recommendations = characterData
281 .getRecommendationURLList();
282 if (recommendations != null) {
283 Element nodeRecommendations = doc.createElementNS(NS,
285 for (RecommendationURL recommendation : recommendations) {
286 Element nodeRecommendation = doc.createElementNS(NS,
288 String displayName = recommendation.getDisplayName();
289 String url = recommendation.getUrl();
291 Element nodeDescription = doc
292 .createElementNS(NS, "description");
293 Attr attrRecommendationDescriptionLang = doc.createAttributeNS(
294 XMLConstants.XML_NS_URI, "lang");
295 attrRecommendationDescriptionLang.setValue(lang);
297 .setAttributeNodeNS(attrRecommendationDescriptionLang);
298 nodeDescription.setTextContent(displayName);
300 Element nodeURL = doc.createElementNS(NS, "URL");
301 Attr attrRecommendationURLLang = doc.createAttributeNS(
302 XMLConstants.XML_NS_URI, "lang");
303 attrRecommendationURLLang.setValue(lang);
304 nodeURL.setAttributeNodeNS(attrRecommendationURLLang);
305 nodeURL.setTextContent(url);
307 nodeRecommendation.appendChild(nodeDescription);
308 nodeRecommendation.appendChild(nodeURL);
310 nodeRecommendations.appendChild(nodeRecommendation);
312 root.appendChild(nodeRecommendations);
315 // presetsのelementを構築する.
316 Element nodePresets = doc.createElementNS(NS, "presets");
317 if (writePartsSetElements(doc, nodePresets, characterData, true, false) > 0) {
318 root.appendChild(nodePresets);
322 TransformerFactory txFactory = TransformerFactory.newInstance();
323 txFactory.setAttribute("indent-number", Integer.valueOf(4));
326 tfmr = txFactory.newTransformer();
327 } catch (TransformerConfigurationException ex) {
328 throw new RuntimeException("JAXP Configuration Failed.", ex);
330 tfmr.setOutputProperty(OutputKeys.INDENT, "yes");
332 // JDK-4504745 : javax.xml.transform.Transformer encoding does not work properly
333 // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4504745
334 final String encoding = "UTF-8";
335 tfmr.setOutputProperty("encoding", encoding);
337 tfmr.transform(new DOMSource(doc), new StreamResult(
338 new OutputStreamWriter(outstm, Charset.forName(encoding))));
340 } catch (TransformerException ex) {
341 IOException ex2 = new IOException("XML Convert failed.");
348 * キャラクターデータ内のPresetおよびFavotiesのPartssetの双方共通のパーツセット要素のリストを構築する.
351 * ドキュメントオブジェクト(createElementNS用)
353 * 親要素、キャラクターデータの場合はPreset、Favoritesの場合はPartssetを示す要素
354 * @param characterData
356 * @param writePresets
357 * Preset属性のパーツセットを登録する場合はtrue、Preset属性時はデフォルトプリセット属性も(あれば)登録される
358 * @param writeFavorites
359 * Preset属性のないパーツセットを登録する場合はtrue
360 * @return 登録したパーツセットの個数
362 protected int writePartsSetElements(Document doc, Element baseElement,
363 CharacterData characterData, boolean writePresets,
364 boolean writeFavorites) {
365 Map<String, PartsSet> partsSetMap = characterData.getPartsSets();
367 Locale locale = Locale.getDefault();
368 String lang = locale.getLanguage();
370 HashMap<String, PartsSet> registeredPartsSetMap = new HashMap<String, PartsSet>();
372 for (Map.Entry<String, PartsSet> partsSetsEntry : partsSetMap
374 PartsSet partsSet = partsSetsEntry.getValue();
375 if (partsSet.isPresetParts() && !writePresets) {
378 if (!partsSet.isPresetParts() && !writeFavorites) {
382 if (partsSet.isEmpty()) {
387 Element nodePreset = createPartsSetXML(doc, lang, partsSet);
388 baseElement.appendChild(nodePreset);
389 registeredPartsSetMap.put(partsSet.getPartsSetId(), partsSet);
392 // プリセット登録時はデフォルトのプリセットIDがあれば、それも登録する.
393 // (ただし、該当パーツセットが書き込み済みである場合のみ)
395 String defaultPresetId = characterData.getDefaultPartsSetId();
396 if (defaultPresetId != null && defaultPresetId.length() > 0) {
397 PartsSet defaultPartsSet = registeredPartsSetMap
398 .get(defaultPresetId);
399 if (defaultPartsSet != null && defaultPartsSet.isPresetParts()) {
400 baseElement.setAttribute("default-preset", defaultPresetId);
405 return registeredPartsSetMap.size();
409 * パーツセットのXM要素を生成して返す.
414 * 言語識別用(パーツセット名などの登録時のlang属性に必要)
416 * パーツセット、nullの場合はxsi:nul="true"が返される.
417 * @return パーツセット1つ分のXML要素
419 public Element createPartsSetXML(Document doc, String lang, PartsSet partsSet) {
420 if (doc == null || lang == null) {
421 throw new IllegalArgumentException();
424 String partsSetId = partsSet.getPartsSetId();
425 String localizedName = partsSet.getLocalizedName();
427 Element nodePreset = doc.createElementNS(NS, "preset");
428 if (partsSet == null || partsSet.isEmpty()) {
429 // 指定されていないか有効でない場合は無しとみなす.
430 nodePreset.setAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "xsi:nil", "true");
434 nodePreset.setAttribute("id", partsSetId);
437 Element nodeName = doc.createElementNS(NS, "display-name");
438 Attr attrLang = doc.createAttributeNS(XMLConstants.XML_NS_URI,
440 attrLang.setValue(lang);
441 nodeName.setAttributeNode(attrLang);
442 nodeName.setTextContent(localizedName);
443 nodePreset.appendChild(nodeName);
446 Color bgColor = partsSet.getBgColor();
447 if (bgColor != null) {
448 Element nodeBgColor = doc.createElementNS(NS,
450 nodeBgColor.setAttribute("color",
451 "#" + Integer.toHexString(bgColor.getRGB() & 0xffffff));
452 nodePreset.appendChild(nodeBgColor);
455 // affine transform parameter
456 double[] affineTransformParameter = partsSet
457 .getAffineTransformParameter();
458 if (affineTransformParameter != null) {
459 Element nodeAffineTransform = doc.createElementNS(NS,
460 "affine-transform-parameter");
461 StringBuilder tmp = new StringBuilder();
462 for (double affineItem : affineTransformParameter) {
463 if (tmp.length() > 0) {
466 tmp.append(Double.toString(affineItem));
468 nodeAffineTransform.setTextContent(tmp.toString());
469 nodePreset.appendChild(nodeAffineTransform);
473 for (Map.Entry<PartsCategory, List<PartsIdentifier>> entry : partsSet
475 PartsCategory partsCategory = entry.getKey();
478 Element nodeCategory = doc.createElementNS(NS, "category");
479 nodeCategory.setAttribute("refid",
480 partsCategory.getCategoryId());
481 nodePreset.appendChild(nodeCategory);
483 List<PartsIdentifier> partsIdentifiers = entry.getValue();
484 for (PartsIdentifier partsIdentifier : partsIdentifiers) {
485 String partsName = partsIdentifier.getPartsName();
486 Element nodeParts = doc.createElementNS(NS, "parts");
487 nodeParts.setAttribute("name", partsName);
488 nodeCategory.appendChild(nodeParts);
490 PartsColorInfo partsColorInfo = partsSet
491 .getColorInfo(partsIdentifier);
492 if (partsColorInfo != null) {
493 Element nodeColor = createPartsColorInfoXML(doc, partsColorInfo);
494 nodeParts.appendChild(nodeColor);
500 Set<String> patternIds = partsSet.getActiveCustomLayerPatternIds();
501 if (patternIds != null) {
502 Element patternsElm = doc.createElementNS(NS, "active-custom-layer-patterns");
503 for (String patternId : patternIds) {
504 Element patElm = doc.createElementNS(NS, "pattern");
505 patElm.setAttribute("id", patternId);
506 patternsElm.appendChild(patElm);
508 nodePreset.appendChild(patternsElm);
515 * パーツカラー情報のXML要素を生成して返す.<br>
519 * @param partsColorInfo
521 * @return パーツカラー情報の要素
523 public Element createPartsColorInfoXML(Document doc, PartsColorInfo partsColorInfo) {
524 if (doc == null || partsColorInfo == null) {
525 throw new IllegalArgumentException();
528 Element nodeColor = doc.createElementNS(NS, "color");
530 for (Map.Entry<Layer, ColorInfo> colorInfoEntry : partsColorInfo
532 Layer layer = colorInfoEntry.getKey();
533 ColorInfo colorInfo = colorInfoEntry.getValue();
535 Element nodeLayer = doc
536 .createElementNS(NS, "layer");
537 nodeLayer.setAttribute("refid", layer.getId());
538 nodeColor.appendChild(nodeLayer);
541 ColorGroup colorGroup = colorInfo.getColorGroup();
542 boolean colorSync = colorInfo.isSyncColorGroup();
544 if (colorGroup.isEnabled()) {
545 Element nodeColorGroup = doc.createElementNS(
547 nodeColorGroup.setAttribute("group",
549 nodeColorGroup.setAttribute("synchronized",
550 colorSync ? "true" : "false");
551 nodeLayer.appendChild(nodeColorGroup);
555 ColorConvertParameter param = colorInfo
556 .getColorParameter();
558 Element nodeRGB = doc.createElementNS(NS, "rgb");
559 Object[][] rgbArgss = {
560 {"red", param.getOffsetR(),
563 {"green", param.getOffsetG(),
566 {"blue", param.getOffsetB(),
569 {"alpha", param.getOffsetA(),
571 param.getGammaA()},};
572 for (Object[] rgbArgs : rgbArgss) {
573 Element nodeRGBItem = doc.createElementNS(NS,
574 rgbArgs[0].toString());
575 nodeRGBItem.setAttribute("offset",
576 rgbArgs[1].toString());
577 nodeRGBItem.setAttribute("factor",
578 rgbArgs[2].toString());
579 nodeRGBItem.setAttribute("gamma",
580 rgbArgs[3].toString());
581 nodeRGB.appendChild(nodeRGBItem);
583 nodeLayer.appendChild(nodeRGB);
586 Element nodeHSB = doc.createElementNS(NS, "hsb");
587 nodeHSB.setAttribute("hue",
588 Float.toString(param.getHue()));
589 nodeHSB.setAttribute("saturation",
590 Float.toString(param.getSaturation()));
591 nodeHSB.setAttribute("brightness",
592 Float.toString(param.getBrightness()));
593 if (param.getContrast() != 0.f) {
594 // ver0.96追加、optional
596 nodeHSB.setAttribute("contrast",
597 Float.toString(param.getContrast()));
599 nodeLayer.appendChild(nodeHSB);
602 Element nodeRGBReplace = doc.createElementNS(NS,
604 ColorConv colorConv = param.getColorReplace();
605 if (colorConv == null) {
606 colorConv = ColorConv.NONE;
608 nodeRGBReplace.setAttribute("replace-type",
610 nodeRGBReplace.setAttribute("gray",
611 Float.toString(param.getGrayLevel()));
612 nodeLayer.appendChild(nodeRGBReplace);
618 public void saveFavorites(CharacterData characterData,
619 OutputStream outstm) throws IOException {
620 if (characterData == null || outstm == null) {
621 throw new IllegalArgumentException();
626 DocumentBuilderFactory factory = DocumentBuilderFactory
628 factory.setNamespaceAware(true);
629 DocumentBuilder builder = factory.newDocumentBuilder();
630 doc = builder.newDocument();
632 } catch (ParserConfigurationException ex) {
633 throw new RuntimeException("JAXP Configuration Exception.", ex);
636 Element root = doc.createElementNS(NS, "partssets");
638 root.setAttribute("xmlns:xml", XMLConstants.XML_NS_URI);
639 root.setAttribute("xmlns:xsi",
640 "http://www.w3.org/2001/XMLSchema-instance");
641 root.setAttribute("xsi:schemaLocation", NS + " partsset.xsd");
642 doc.appendChild(root);
644 // presetsのelementを構築する.(Presetは除く)
645 writePartsSetElements(doc, root, characterData, false, true);
648 TransformerFactory txFactory = TransformerFactory.newInstance();
649 txFactory.setAttribute("indent-number", Integer.valueOf(4));
652 tfmr = txFactory.newTransformer();
653 } catch (TransformerConfigurationException ex) {
654 throw new RuntimeException("JAXP Configuration Failed.", ex);
656 tfmr.setOutputProperty(OutputKeys.INDENT, "yes");
658 // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4504745
659 final String encoding = "UTF-8";
660 tfmr.setOutputProperty("encoding", encoding);
662 tfmr.transform(new DOMSource(doc), new StreamResult(
663 new OutputStreamWriter(outstm, Charset.forName(encoding))));
665 } catch (TransformerException ex) {
666 IOException ex2 = new IOException("XML Convert failed.");