ワーキングセットをXMLで出力できるようにした。(読み込みは未対応)
@@ -400,6 +400,60 @@ | ||
400 | 400 | } |
401 | 401 | |
402 | 402 | /** |
403 | + * キャラクターデータの構造を表す文字列を返す.<br> | |
404 | + * カテゴリ、レイヤー、色グループのみで構成される.<br> | |
405 | + * id, revなどは含まない.<br> | |
406 | + * @return キャラクターデータの構造を表す文字列 | |
407 | + */ | |
408 | + public String toStructureString() { | |
409 | + // カラーグループ | |
410 | + StringBuilder buf = new StringBuilder(); | |
411 | + buf.append("{colorGroup:["); | |
412 | + for (ColorGroup colorGroup : getColorGroups()) { | |
413 | + buf.append(colorGroup.getId()); | |
414 | + buf.append(","); | |
415 | + } | |
416 | + buf.append("],"); | |
417 | + | |
418 | + // カテゴリ | |
419 | + buf.append("category:["); | |
420 | + for (PartsCategory category : getPartsCategories()) { | |
421 | + buf.append("{id:"); | |
422 | + buf.append(category.getCategoryId()); | |
423 | + | |
424 | + buf.append(",layer:["); | |
425 | + for (Layer layer : category.getLayers()) { | |
426 | + buf.append("{id:"); | |
427 | + buf.append(layer.getId()); | |
428 | + buf.append(",dir:"); | |
429 | + buf.append(layer.getDir()); | |
430 | + buf.append("},"); | |
431 | + } | |
432 | + buf.append("]},"); | |
433 | + } | |
434 | + buf.append("]}"); | |
435 | + | |
436 | + return buf.toString(); | |
437 | + } | |
438 | + | |
439 | + /** | |
440 | + * キャラクターデータのID, REVと構造を識別するシグネチャの文字列を返す.<br> | |
441 | + * (構造はカテゴリ、レイヤー、色グループのみ).<br> | |
442 | + * @return シグネチャの文字列 | |
443 | + */ | |
444 | + public String toSignatureString() { | |
445 | + StringBuilder buf = new StringBuilder(); | |
446 | + buf.append("{id:"); | |
447 | + buf.append(getId()); | |
448 | + buf.append(",rev:"); | |
449 | + buf.append(getRev()); | |
450 | + buf.append(",structure:"); | |
451 | + buf.append(toStructureString()); | |
452 | + buf.append("}"); | |
453 | + return buf.toString(); | |
454 | + } | |
455 | + | |
456 | + /** | |
403 | 457 | * デシリアライズする. |
404 | 458 | * @param stream 入力もと |
405 | 459 | * @throws IOException 失敗 |
@@ -54,8 +54,18 @@ | ||
54 | 54 | /** |
55 | 55 | * キャラクター定義XMLファイルの名前空間 |
56 | 56 | */ |
57 | - private static final String NS = "http://charactermanaj.sourceforge.jp/schema/charactermanaj"; | |
57 | + private static final String DEFAULT_NS = "http://charactermanaj.sourceforge.jp/schema/charactermanaj"; | |
58 | 58 | |
59 | + private final String NS; | |
60 | + | |
61 | + public CharacterDataXMLWriter() { | |
62 | + this(DEFAULT_NS); | |
63 | + } | |
64 | + | |
65 | + public CharacterDataXMLWriter(String ns) { | |
66 | + this.NS = ns; | |
67 | + } | |
68 | + | |
59 | 69 | /** |
60 | 70 | * キャラクターデータのXMLの書き込み. |
61 | 71 | * |
@@ -87,8 +97,7 @@ | ||
87 | 97 | root.setAttribute("version", VERSION_SIG_1_0); |
88 | 98 | |
89 | 99 | root.setAttribute("xmlns:xml", XMLConstants.XML_NS_URI); |
90 | - root.setAttribute("xmlns:xsi", | |
91 | - "http://www.w3.org/2001/XMLSchema-instance"); | |
100 | + root.setAttribute("xmlns:xsi", XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI); | |
92 | 101 | root.setAttribute("xsi:schemaLocation", NS + " character.xsd"); |
93 | 102 | root.setAttribute("id", characterData.getId()); |
94 | 103 | root.setAttribute("rev", characterData.getRev()); |
@@ -316,6 +325,7 @@ | ||
316 | 325 | } |
317 | 326 | tfmr.setOutputProperty(OutputKeys.INDENT, "yes"); |
318 | 327 | |
328 | + // JDK-4504745 : javax.xml.transform.Transformer encoding does not work properly | |
319 | 329 | // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4504745 |
320 | 330 | final String encoding = "UTF-8"; |
321 | 331 | tfmr.setOutputProperty("encoding", encoding); |
@@ -370,177 +380,216 @@ | ||
370 | 380 | continue; |
371 | 381 | } |
372 | 382 | |
373 | - String partsSetId = partsSet.getPartsSetId(); | |
374 | - String localizedName = partsSet.getLocalizedName(); | |
375 | - | |
376 | - Element nodePreset = doc.createElementNS(NS, "preset"); | |
377 | - nodePreset.setAttribute("id", partsSetId); | |
383 | + Element nodePreset = createPartsSetXML(doc, lang, partsSet); | |
378 | 384 | baseElement.appendChild(nodePreset); |
379 | 385 | registeredPartsSetMap.put(partsSet.getPartsSetId(), partsSet); |
386 | + } | |
380 | 387 | |
381 | - // display-name | |
382 | - Element nodeName = doc.createElementNS(NS, "display-name"); | |
383 | - Attr attrLang = doc.createAttributeNS(XMLConstants.XML_NS_URI, | |
384 | - "lang"); | |
385 | - attrLang.setValue(lang); | |
386 | - nodeName.setAttributeNode(attrLang); | |
387 | - nodeName.setTextContent(localizedName); | |
388 | - nodePreset.appendChild(nodeName); | |
389 | - | |
390 | - // bgColor | |
391 | - Color bgColor = partsSet.getBgColor(); | |
392 | - if (bgColor != null) { | |
393 | - Element nodeBgColor = doc.createElementNS(NS, | |
394 | - "background-color"); | |
395 | - nodeBgColor.setAttribute("color", | |
396 | - "#" + Integer.toHexString(bgColor.getRGB() & 0xffffff)); | |
397 | - nodePreset.appendChild(nodeBgColor); | |
388 | + // プリセット登録時はデフォルトのプリセットIDがあれば、それも登録する. | |
389 | + // (ただし、該当パーツセットが書き込み済みである場合のみ) | |
390 | + if (writePresets) { | |
391 | + String defaultPresetId = characterData.getDefaultPartsSetId(); | |
392 | + if (defaultPresetId != null && defaultPresetId.length() > 0) { | |
393 | + PartsSet defaultPartsSet = registeredPartsSetMap | |
394 | + .get(defaultPresetId); | |
395 | + if (defaultPartsSet != null && defaultPartsSet.isPresetParts()) { | |
396 | + baseElement.setAttribute("default-preset", defaultPresetId); | |
397 | + } | |
398 | 398 | } |
399 | + } | |
399 | 400 | |
400 | - // affine transform parameter | |
401 | - double[] affineTransformParameter = partsSet | |
402 | - .getAffineTransformParameter(); | |
403 | - if (affineTransformParameter != null) { | |
404 | - Element nodeAffineTransform = doc.createElementNS(NS, | |
405 | - "affine-transform-parameter"); | |
406 | - StringBuilder tmp = new StringBuilder(); | |
407 | - for (double affineItem : affineTransformParameter) { | |
408 | - if (tmp.length() > 0) { | |
409 | - tmp.append(" "); | |
410 | - } | |
411 | - tmp.append(Double.toString(affineItem)); | |
401 | + return registeredPartsSetMap.size(); | |
402 | + } | |
403 | + | |
404 | + /** | |
405 | + * パーツセットのXM要素を生成して返す. | |
406 | + * @param doc ノードを生成するためのファクトリ | |
407 | + * @param lang 言語識別用(パーツセット名などの登録時のlang属性に必要) | |
408 | + * @param partsSet パーツセット、nullの場合はxsi:nul="true"が返される. | |
409 | + * @return パーツセット1つ分のXML要素 | |
410 | + */ | |
411 | + public Element createPartsSetXML(Document doc, String lang, PartsSet partsSet) { | |
412 | + if (doc == null || lang == null) { | |
413 | + throw new IllegalArgumentException(); | |
414 | + } | |
415 | + | |
416 | + String partsSetId = partsSet.getPartsSetId(); | |
417 | + String localizedName = partsSet.getLocalizedName(); | |
418 | + | |
419 | + Element nodePreset = doc.createElementNS(NS, "preset"); | |
420 | + if (partsSet == null || partsSet.isEmpty()) { | |
421 | + // 指定されていないか有効でない場合は無しとみなす. | |
422 | + nodePreset.setAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "xsi:nil", "true"); | |
423 | + return nodePreset; | |
424 | + } | |
425 | + | |
426 | + nodePreset.setAttribute("id", partsSetId); | |
427 | + | |
428 | + // display-name | |
429 | + Element nodeName = doc.createElementNS(NS, "display-name"); | |
430 | + Attr attrLang = doc.createAttributeNS(XMLConstants.XML_NS_URI, | |
431 | + "lang"); | |
432 | + attrLang.setValue(lang); | |
433 | + nodeName.setAttributeNode(attrLang); | |
434 | + nodeName.setTextContent(localizedName); | |
435 | + nodePreset.appendChild(nodeName); | |
436 | + | |
437 | + // bgColor | |
438 | + Color bgColor = partsSet.getBgColor(); | |
439 | + if (bgColor != null) { | |
440 | + Element nodeBgColor = doc.createElementNS(NS, | |
441 | + "background-color"); | |
442 | + nodeBgColor.setAttribute("color", | |
443 | + "#" + Integer.toHexString(bgColor.getRGB() & 0xffffff)); | |
444 | + nodePreset.appendChild(nodeBgColor); | |
445 | + } | |
446 | + | |
447 | + // affine transform parameter | |
448 | + double[] affineTransformParameter = partsSet | |
449 | + .getAffineTransformParameter(); | |
450 | + if (affineTransformParameter != null) { | |
451 | + Element nodeAffineTransform = doc.createElementNS(NS, | |
452 | + "affine-transform-parameter"); | |
453 | + StringBuilder tmp = new StringBuilder(); | |
454 | + for (double affineItem : affineTransformParameter) { | |
455 | + if (tmp.length() > 0) { | |
456 | + tmp.append(" "); | |
412 | 457 | } |
413 | - nodeAffineTransform.setTextContent(tmp.toString()); | |
414 | - nodePreset.appendChild(nodeAffineTransform); | |
458 | + tmp.append(Double.toString(affineItem)); | |
415 | 459 | } |
460 | + nodeAffineTransform.setTextContent(tmp.toString()); | |
461 | + nodePreset.appendChild(nodeAffineTransform); | |
462 | + } | |
416 | 463 | |
417 | - // categories | |
418 | - for (Map.Entry<PartsCategory, List<PartsIdentifier>> entry : partsSet | |
419 | - .entrySet()) { | |
420 | - PartsCategory partsCategory = entry.getKey(); | |
464 | + // categories | |
465 | + for (Map.Entry<PartsCategory, List<PartsIdentifier>> entry : partsSet | |
466 | + .entrySet()) { | |
467 | + PartsCategory partsCategory = entry.getKey(); | |
421 | 468 | |
422 | - // category | |
423 | - Element nodeCategory = doc.createElementNS(NS, "category"); | |
424 | - nodeCategory.setAttribute("refid", | |
425 | - partsCategory.getCategoryId()); | |
426 | - nodePreset.appendChild(nodeCategory); | |
469 | + // category | |
470 | + Element nodeCategory = doc.createElementNS(NS, "category"); | |
471 | + nodeCategory.setAttribute("refid", | |
472 | + partsCategory.getCategoryId()); | |
473 | + nodePreset.appendChild(nodeCategory); | |
427 | 474 | |
428 | - List<PartsIdentifier> partsIdentifiers = entry.getValue(); | |
429 | - for (PartsIdentifier partsIdentifier : partsIdentifiers) { | |
430 | - String partsName = partsIdentifier.getPartsName(); | |
431 | - Element nodeParts = doc.createElementNS(NS, "parts"); | |
432 | - nodeParts.setAttribute("name", partsName); | |
433 | - nodeCategory.appendChild(nodeParts); | |
475 | + List<PartsIdentifier> partsIdentifiers = entry.getValue(); | |
476 | + for (PartsIdentifier partsIdentifier : partsIdentifiers) { | |
477 | + String partsName = partsIdentifier.getPartsName(); | |
478 | + Element nodeParts = doc.createElementNS(NS, "parts"); | |
479 | + nodeParts.setAttribute("name", partsName); | |
480 | + nodeCategory.appendChild(nodeParts); | |
434 | 481 | |
435 | - PartsColorInfo partsColorInfo = partsSet | |
436 | - .getColorInfo(partsIdentifier); | |
437 | - if (partsColorInfo != null) { | |
438 | - Element nodeColor = doc.createElementNS(NS, "color"); | |
439 | - nodeParts.appendChild(nodeColor); | |
482 | + PartsColorInfo partsColorInfo = partsSet | |
483 | + .getColorInfo(partsIdentifier); | |
484 | + if (partsColorInfo != null) { | |
485 | + Element nodeColor = createPartsColorInfoXML(doc, partsColorInfo); | |
486 | + nodeParts.appendChild(nodeColor); | |
487 | + } | |
488 | + } | |
489 | + } | |
490 | + | |
491 | + return nodePreset; | |
492 | + } | |
493 | + | |
494 | + /** | |
495 | + * パーツカラー情報のXML要素を生成して返す.<br> | |
496 | + * @param doc 要素を作成するためのファクトリ | |
497 | + * @param partsColorInfo パーツカラー情報 | |
498 | + * @return パーツカラー情報の要素 | |
499 | + */ | |
500 | + public Element createPartsColorInfoXML(Document doc, PartsColorInfo partsColorInfo) { | |
501 | + if (doc == null || partsColorInfo == null) { | |
502 | + throw new IllegalArgumentException(); | |
503 | + } | |
440 | 504 | |
441 | - for (Map.Entry<Layer, ColorInfo> colorInfoEntry : partsColorInfo | |
442 | - .entrySet()) { | |
443 | - Layer layer = colorInfoEntry.getKey(); | |
444 | - ColorInfo colorInfo = colorInfoEntry.getValue(); | |
505 | + Element nodeColor = doc.createElementNS(NS, "color"); | |
445 | 506 | |
446 | - Element nodeLayer = doc | |
447 | - .createElementNS(NS, "layer"); | |
448 | - nodeLayer.setAttribute("refid", layer.getId()); | |
449 | - nodeColor.appendChild(nodeLayer); | |
507 | + for (Map.Entry<Layer, ColorInfo> colorInfoEntry : partsColorInfo | |
508 | + .entrySet()) { | |
509 | + Layer layer = colorInfoEntry.getKey(); | |
510 | + ColorInfo colorInfo = colorInfoEntry.getValue(); | |
450 | 511 | |
451 | - // ColorGroup | |
452 | - ColorGroup colorGroup = colorInfo.getColorGroup(); | |
453 | - boolean colorSync = colorInfo.isSyncColorGroup(); | |
512 | + Element nodeLayer = doc | |
513 | + .createElementNS(NS, "layer"); | |
514 | + nodeLayer.setAttribute("refid", layer.getId()); | |
515 | + nodeColor.appendChild(nodeLayer); | |
454 | 516 | |
455 | - if (colorGroup.isEnabled()) { | |
456 | - Element nodeColorGroup = doc.createElementNS( | |
457 | - NS, "color-group"); | |
458 | - nodeColorGroup.setAttribute("group", | |
459 | - colorGroup.getId()); | |
460 | - nodeColorGroup.setAttribute("synchronized", | |
461 | - colorSync ? "true" : "false"); | |
462 | - nodeLayer.appendChild(nodeColorGroup); | |
463 | - } | |
517 | + // ColorGroup | |
518 | + ColorGroup colorGroup = colorInfo.getColorGroup(); | |
519 | + boolean colorSync = colorInfo.isSyncColorGroup(); | |
464 | 520 | |
465 | - // RGB | |
466 | - ColorConvertParameter param = colorInfo | |
467 | - .getColorParameter(); | |
521 | + if (colorGroup.isEnabled()) { | |
522 | + Element nodeColorGroup = doc.createElementNS( | |
523 | + NS, "color-group"); | |
524 | + nodeColorGroup.setAttribute("group", | |
525 | + colorGroup.getId()); | |
526 | + nodeColorGroup.setAttribute("synchronized", | |
527 | + colorSync ? "true" : "false"); | |
528 | + nodeLayer.appendChild(nodeColorGroup); | |
529 | + } | |
468 | 530 | |
469 | - Element nodeRGB = doc.createElementNS(NS, "rgb"); | |
470 | - Object[][] rgbArgss = { | |
471 | - {"red", param.getOffsetR(), | |
472 | - param.getFactorR(), | |
473 | - param.getGammaR()}, | |
474 | - {"green", param.getOffsetG(), | |
475 | - param.getFactorG(), | |
476 | - param.getGammaG()}, | |
477 | - {"blue", param.getOffsetB(), | |
478 | - param.getFactorB(), | |
479 | - param.getGammaB()}, | |
480 | - {"alpha", param.getOffsetA(), | |
481 | - param.getFactorA(), | |
482 | - param.getGammaA()},}; | |
483 | - for (Object[] rgbArgs : rgbArgss) { | |
484 | - Element nodeRGBItem = doc.createElementNS(NS, | |
485 | - rgbArgs[0].toString()); | |
486 | - nodeRGBItem.setAttribute("offset", | |
487 | - rgbArgs[1].toString()); | |
488 | - nodeRGBItem.setAttribute("factor", | |
489 | - rgbArgs[2].toString()); | |
490 | - nodeRGBItem.setAttribute("gamma", | |
491 | - rgbArgs[3].toString()); | |
492 | - nodeRGB.appendChild(nodeRGBItem); | |
493 | - } | |
494 | - nodeLayer.appendChild(nodeRGB); | |
531 | + // RGB | |
532 | + ColorConvertParameter param = colorInfo | |
533 | + .getColorParameter(); | |
495 | 534 | |
496 | - // HSB | |
497 | - Element nodeHSB = doc.createElementNS(NS, "hsb"); | |
498 | - nodeHSB.setAttribute("hue", | |
499 | - Float.toString(param.getHue())); | |
500 | - nodeHSB.setAttribute("saturation", | |
501 | - Float.toString(param.getSaturation())); | |
502 | - nodeHSB.setAttribute("brightness", | |
503 | - Float.toString(param.getBrightness())); | |
504 | - if (param.getContrast() != 0.f) { | |
505 | - // ver0.96追加、optional | |
506 | - // ぴったり0.0fだったら省略する. | |
507 | - nodeHSB.setAttribute("contrast", | |
508 | - Float.toString(param.getContrast())); | |
509 | - } | |
510 | - nodeLayer.appendChild(nodeHSB); | |
535 | + Element nodeRGB = doc.createElementNS(NS, "rgb"); | |
536 | + Object[][] rgbArgss = { | |
537 | + {"red", param.getOffsetR(), | |
538 | + param.getFactorR(), | |
539 | + param.getGammaR()}, | |
540 | + {"green", param.getOffsetG(), | |
541 | + param.getFactorG(), | |
542 | + param.getGammaG()}, | |
543 | + {"blue", param.getOffsetB(), | |
544 | + param.getFactorB(), | |
545 | + param.getGammaB()}, | |
546 | + {"alpha", param.getOffsetA(), | |
547 | + param.getFactorA(), | |
548 | + param.getGammaA()},}; | |
549 | + for (Object[] rgbArgs : rgbArgss) { | |
550 | + Element nodeRGBItem = doc.createElementNS(NS, | |
551 | + rgbArgs[0].toString()); | |
552 | + nodeRGBItem.setAttribute("offset", | |
553 | + rgbArgs[1].toString()); | |
554 | + nodeRGBItem.setAttribute("factor", | |
555 | + rgbArgs[2].toString()); | |
556 | + nodeRGBItem.setAttribute("gamma", | |
557 | + rgbArgs[3].toString()); | |
558 | + nodeRGB.appendChild(nodeRGBItem); | |
559 | + } | |
560 | + nodeLayer.appendChild(nodeRGB); | |
511 | 561 | |
512 | - // RGB Replace | |
513 | - Element nodeRGBReplace = doc.createElementNS(NS, | |
514 | - "rgb-replace"); | |
515 | - ColorConv colorConv = param.getColorReplace(); | |
516 | - if (colorConv == null) { | |
517 | - colorConv = ColorConv.NONE; | |
518 | - } | |
519 | - nodeRGBReplace.setAttribute("replace-type", | |
520 | - colorConv.name()); | |
521 | - nodeRGBReplace.setAttribute("gray", | |
522 | - Float.toString(param.getGrayLevel())); | |
523 | - nodeLayer.appendChild(nodeRGBReplace); | |
524 | - } | |
525 | - } | |
526 | - } | |
562 | + // HSB | |
563 | + Element nodeHSB = doc.createElementNS(NS, "hsb"); | |
564 | + nodeHSB.setAttribute("hue", | |
565 | + Float.toString(param.getHue())); | |
566 | + nodeHSB.setAttribute("saturation", | |
567 | + Float.toString(param.getSaturation())); | |
568 | + nodeHSB.setAttribute("brightness", | |
569 | + Float.toString(param.getBrightness())); | |
570 | + if (param.getContrast() != 0.f) { | |
571 | + // ver0.96追加、optional | |
572 | + // ぴったり0.0fだったら省略する. | |
573 | + nodeHSB.setAttribute("contrast", | |
574 | + Float.toString(param.getContrast())); | |
527 | 575 | } |
528 | - } | |
576 | + nodeLayer.appendChild(nodeHSB); | |
529 | 577 | |
530 | - // プリセット登録時はデフォルトのプリセットIDがあれば、それも登録する. | |
531 | - // (ただし、該当パーツセットが書き込み済みである場合のみ) | |
532 | - if (writePresets) { | |
533 | - String defaultPresetId = characterData.getDefaultPartsSetId(); | |
534 | - if (defaultPresetId != null && defaultPresetId.length() > 0) { | |
535 | - PartsSet defaultPartsSet = registeredPartsSetMap | |
536 | - .get(defaultPresetId); | |
537 | - if (defaultPartsSet != null && defaultPartsSet.isPresetParts()) { | |
538 | - baseElement.setAttribute("default-preset", defaultPresetId); | |
539 | - } | |
578 | + // RGB Replace | |
579 | + Element nodeRGBReplace = doc.createElementNS(NS, | |
580 | + "rgb-replace"); | |
581 | + ColorConv colorConv = param.getColorReplace(); | |
582 | + if (colorConv == null) { | |
583 | + colorConv = ColorConv.NONE; | |
540 | 584 | } |
585 | + nodeRGBReplace.setAttribute("replace-type", | |
586 | + colorConv.name()); | |
587 | + nodeRGBReplace.setAttribute("gray", | |
588 | + Float.toString(param.getGrayLevel())); | |
589 | + nodeLayer.appendChild(nodeRGBReplace); | |
541 | 590 | } |
542 | - | |
543 | - return registeredPartsSetMap.size(); | |
591 | + | |
592 | + return nodeColor; | |
544 | 593 | } |
545 | 594 | |
546 | 595 | public void saveFavorites(CharacterData characterData, |
@@ -0,0 +1,311 @@ | ||
1 | +package charactermanaj.model.io; | |
2 | + | |
3 | +import java.awt.Color; | |
4 | +import java.io.File; | |
5 | +import java.io.IOException; | |
6 | +import java.io.OutputStream; | |
7 | +import java.io.OutputStreamWriter; | |
8 | +import java.net.URI; | |
9 | +import java.nio.charset.Charset; | |
10 | +import java.util.LinkedHashMap; | |
11 | +import java.util.Locale; | |
12 | +import java.util.Map; | |
13 | + | |
14 | +import javax.xml.XMLConstants; | |
15 | +import javax.xml.parsers.DocumentBuilder; | |
16 | +import javax.xml.parsers.DocumentBuilderFactory; | |
17 | +import javax.xml.parsers.ParserConfigurationException; | |
18 | +import javax.xml.transform.OutputKeys; | |
19 | +import javax.xml.transform.Transformer; | |
20 | +import javax.xml.transform.TransformerConfigurationException; | |
21 | +import javax.xml.transform.TransformerException; | |
22 | +import javax.xml.transform.TransformerFactory; | |
23 | +import javax.xml.transform.dom.DOMSource; | |
24 | +import javax.xml.transform.stream.StreamResult; | |
25 | + | |
26 | +import org.w3c.dom.Document; | |
27 | +import org.w3c.dom.Element; | |
28 | + | |
29 | +import charactermanaj.model.CharacterData; | |
30 | +import charactermanaj.model.PartsColorInfo; | |
31 | +import charactermanaj.model.PartsIdentifier; | |
32 | +import charactermanaj.model.PartsSet; | |
33 | +import charactermanaj.model.WorkingSet; | |
34 | +import charactermanaj.ui.model.WallpaperInfo; | |
35 | +import charactermanaj.ui.model.WallpaperInfo.WallpaperResourceType; | |
36 | + | |
37 | +/** | |
38 | + * WorkingSetのXMLへの書き込み | |
39 | + */ | |
40 | +public class WorkingSetXMLWriter { | |
41 | + | |
42 | + /** | |
43 | + * WorkingSetのバージョン | |
44 | + */ | |
45 | + private static final String VERSION_SIG_1_0 = "1.0"; | |
46 | + | |
47 | + /** | |
48 | + * WorkingSetのXMLファイルの名前空間 | |
49 | + */ | |
50 | + private static final String NS = "http://charactermanaj.sourceforge.jp/schema/charactermanaj-workingset"; | |
51 | + | |
52 | + /** | |
53 | + * キャラクターデータのXML化 | |
54 | + */ | |
55 | + private CharacterDataXMLWriter characterDataXmlWriter = | |
56 | + new CharacterDataXMLWriter(NS); | |
57 | + | |
58 | + /** | |
59 | + * ワーキングセットをXML表現で出力ストリームに出力します.<br> | |
60 | + * @param ws ワーキングセット | |
61 | + * @param outstm 出力先ストリーム | |
62 | + * @throws IOException 失敗 | |
63 | + */ | |
64 | + public void writeWorkingSet(WorkingSet ws, OutputStream outstm) throws IOException { | |
65 | + if (ws == null || outstm == null) { | |
66 | + throw new IllegalArgumentException(); | |
67 | + } | |
68 | + | |
69 | + Document doc = createWorkingSetXML(ws); | |
70 | + | |
71 | + // output xml | |
72 | + TransformerFactory txFactory = TransformerFactory.newInstance(); | |
73 | + txFactory.setAttribute("indent-number", Integer.valueOf(4)); | |
74 | + Transformer tfmr; | |
75 | + try { | |
76 | + tfmr = txFactory.newTransformer(); | |
77 | + } catch (TransformerConfigurationException ex) { | |
78 | + throw new RuntimeException("JAXP Configuration Failed.", ex); | |
79 | + } | |
80 | + tfmr.setOutputProperty(OutputKeys.INDENT, "yes"); | |
81 | + | |
82 | + // JDK-4504745 : javax.xml.transform.Transformer encoding does not work properly | |
83 | + // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4504745 | |
84 | + final String encoding = "UTF-8"; | |
85 | + tfmr.setOutputProperty("encoding", encoding); | |
86 | + try { | |
87 | + tfmr.transform(new DOMSource(doc), new StreamResult( | |
88 | + new OutputStreamWriter(outstm, Charset.forName(encoding)))); | |
89 | + | |
90 | + } catch (TransformerException ex) { | |
91 | + IOException ex2 = new IOException("XML Convert failed."); | |
92 | + ex2.initCause(ex); | |
93 | + throw ex2; | |
94 | + } | |
95 | + } | |
96 | + | |
97 | + /** | |
98 | + * ワーキングセットのXMLドキュメントを生成します. | |
99 | + * @param ws ワーキングセット | |
100 | + * @return XMLドキュメント | |
101 | + */ | |
102 | + public Document createWorkingSetXML(WorkingSet ws) { | |
103 | + if (ws == null) { | |
104 | + throw new IllegalArgumentException(); | |
105 | + } | |
106 | + | |
107 | + Document doc; | |
108 | + try { | |
109 | + DocumentBuilderFactory factory = DocumentBuilderFactory | |
110 | + .newInstance(); | |
111 | + factory.setNamespaceAware(true); | |
112 | + DocumentBuilder builder = factory.newDocumentBuilder(); | |
113 | + doc = builder.newDocument(); | |
114 | + } catch (ParserConfigurationException ex) { | |
115 | + throw new RuntimeException("JAXP Configuration failed.", ex); | |
116 | + } | |
117 | + | |
118 | + Locale locale = Locale.getDefault(); | |
119 | + String lang = locale.getLanguage(); | |
120 | + | |
121 | + Element root = doc.createElementNS(NS, "character-workingset"); | |
122 | + root.setAttribute("version", VERSION_SIG_1_0); | |
123 | + | |
124 | + root.setAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, "xmlns:xsi", | |
125 | + XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI); | |
126 | + root.setAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, "xmlns:xml", | |
127 | + XMLConstants.XML_NS_URI); | |
128 | + root.setAttribute("xsi:schemaLocation", NS + " character_ws.xsd"); | |
129 | + | |
130 | + // ドキュメントベース | |
131 | + URI docbase = ws.getCharacterDocBase(); | |
132 | + root.setAttribute("characterDocBase", docbase == null ? "" : docbase.toString()); | |
133 | + | |
134 | + // キャラクターデータのシグネチャ | |
135 | + CharacterData cd = ws.getCharacterData(); | |
136 | + Element characterDataSigElm = doc.createElementNS(NS, "characterDataSig"); | |
137 | + if (cd == null || !cd.isValid()) { | |
138 | + // 指定されていないか有効でない場合は無しとみなす. | |
139 | + characterDataSigElm.setAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "xsi:nil", "true"); | |
140 | + } else { | |
141 | + characterDataSigElm.setTextContent(cd.toSignatureString()); | |
142 | + } | |
143 | + root.appendChild(characterDataSigElm); | |
144 | + | |
145 | + // パーツカラー情報 | |
146 | + root.appendChild(writePartsColorInfoMap(doc, ws.getPartsColorInfoMap())); | |
147 | + | |
148 | + // パーツセット | |
149 | + PartsSet partsSet = ws.getPartsSet(); | |
150 | + Element partsSetElm = doc.createElementNS(NS, "partsSet"); | |
151 | + if (partsSet == null || partsSet.isEmpty()) { | |
152 | + partsSetElm.setAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "xsi:nil", "true"); | |
153 | + | |
154 | + } else { | |
155 | + Element elm = characterDataXmlWriter.createPartsSetXML(doc, lang, partsSet); | |
156 | + partsSetElm.appendChild(elm); | |
157 | + } | |
158 | + root.appendChild(partsSetElm); | |
159 | + | |
160 | + // 最後に使用した保存先ディレクトリ | |
161 | + Element lastUsedSaveDirElm = doc.createElementNS(NS, "lastUsedSaveDir"); | |
162 | + File lastUsedSaveDir = ws.getLastUsedSaveDir(); | |
163 | + if (lastUsedSaveDir == null) { | |
164 | + lastUsedSaveDirElm.setAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "xsi:nil", "true"); | |
165 | + | |
166 | + } else { | |
167 | + lastUsedSaveDirElm.setTextContent(lastUsedSaveDir.getPath()); | |
168 | + } | |
169 | + root.appendChild(lastUsedSaveDirElm); | |
170 | + | |
171 | + // 最後に使用したエクスポート先ディレクトリ | |
172 | + Element lastUsedExportDirElm = doc.createElementNS(NS, "lastUsedExportDir"); | |
173 | + File lastUsedExportDir = ws.getLastUsedExportDir(); | |
174 | + if (lastUsedExportDir == null) { | |
175 | + lastUsedExportDirElm.setAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "xsi:nil", "true"); | |
176 | + | |
177 | + } else { | |
178 | + lastUsedExportDirElm.setTextContent(lastUsedExportDir.getPath()); | |
179 | + } | |
180 | + root.appendChild(lastUsedExportDirElm); | |
181 | + | |
182 | + // 最後に使用したパーツセット情報、なければnull | |
183 | + PartsSet lastUsePresetParts = ws.getLastUsePresetParts(); | |
184 | + Element lastUsePresetPartsElm = doc.createElementNS(NS, "lastUsePresetParts"); | |
185 | + if (lastUsePresetParts == null || lastUsePresetParts.isEmpty()) { | |
186 | + lastUsePresetPartsElm.setAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "xsi:nil", "true"); | |
187 | + | |
188 | + } else { | |
189 | + Element elm = characterDataXmlWriter.createPartsSetXML(doc, lang, lastUsePresetParts); | |
190 | + lastUsePresetPartsElm.appendChild(elm); | |
191 | + } | |
192 | + root.appendChild(lastUsePresetPartsElm); | |
193 | + | |
194 | + // 壁紙情報 | |
195 | + root.appendChild(writeWallpaper(doc, ws.getWallpaperInfo())); | |
196 | + | |
197 | + doc.appendChild(root); | |
198 | + return doc; | |
199 | + } | |
200 | + | |
201 | + /** | |
202 | + * パーツごとのカラー情報のXML要素を生成して返します. | |
203 | + * @param doc 要素のファクトリ | |
204 | + * @param partsColorMap パーツごとのカラー情報のマップ | |
205 | + * @return パーツごとのカラー情報のXML要素 | |
206 | + */ | |
207 | + public Element writePartsColorInfoMap(Document doc, Map<PartsIdentifier, PartsColorInfo> partsColorMap) { | |
208 | + Element partsColorInfoMapElm = doc.createElementNS(NS, "partsColorInfoMap"); | |
209 | + if (partsColorMap != null) { | |
210 | + // 使用しているカラーの設定ごとに番号を振る | |
211 | + // (同じカラー設定であれば同じ番号とする.) | |
212 | + LinkedHashMap<PartsColorInfo, String> colorMap = new LinkedHashMap<PartsColorInfo, String>(); | |
213 | + for (Map.Entry<PartsIdentifier, PartsColorInfo> partsColorEntry : partsColorMap.entrySet()) { | |
214 | + PartsColorInfo partsColorInfo = partsColorEntry.getValue(); | |
215 | + if (partsColorInfo != null && !partsColorInfo.isEmpty()) { | |
216 | + if (!colorMap.containsKey(partsColorInfo)) { | |
217 | + colorMap.put(partsColorInfo, Integer.toString(colorMap.size() + 1)); | |
218 | + } | |
219 | + } | |
220 | + } | |
221 | + | |
222 | + // すべてのカラー設定を出力する. | |
223 | + Element colorsElm = doc.createElementNS(NS, "colors"); | |
224 | + for (Map.Entry<PartsColorInfo, String> colorMapEntry : colorMap.entrySet()) { | |
225 | + PartsColorInfo partsColorInfo = colorMapEntry.getKey(); | |
226 | + String id = colorMapEntry.getValue(); | |
227 | + Element partsColorElm = characterDataXmlWriter.createPartsColorInfoXML(doc, partsColorInfo); | |
228 | + partsColorElm.setAttribute("id", id); | |
229 | + colorsElm.appendChild(partsColorElm); | |
230 | + } | |
231 | + partsColorInfoMapElm.appendChild(colorsElm); | |
232 | + | |
233 | + // パーツと、そのパーツのカラー設定番号の一覧を出力する. | |
234 | + Element partsListElm = doc.createElementNS(NS, "partsList"); | |
235 | + for (Map.Entry<PartsIdentifier, PartsColorInfo> partsColorEntry : partsColorMap.entrySet()) { | |
236 | + PartsIdentifier partsIdentifier = partsColorEntry.getKey(); | |
237 | + PartsColorInfo partsColorInfo = partsColorEntry.getValue(); | |
238 | + if (partsColorInfo != null && !partsColorInfo.isEmpty()) { | |
239 | + String colorId = colorMap.get(partsColorInfo); | |
240 | + if (colorId == null) { | |
241 | + throw new RuntimeException("colorMapが不整合です"); | |
242 | + } | |
243 | + Element partsElm = doc.createElementNS(NS, "partsIdentifier"); | |
244 | + String categoryId = partsIdentifier.getPartsCategory().getCategoryId(); | |
245 | + partsElm.setAttribute("categoryId", categoryId); | |
246 | + partsElm.setAttribute("name", partsIdentifier.getPartsName()); | |
247 | + partsElm.setAttribute("colorId", colorId); | |
248 | + partsListElm.appendChild(partsElm); | |
249 | + } | |
250 | + } | |
251 | + partsColorInfoMapElm.appendChild(partsListElm); | |
252 | + } | |
253 | + return partsColorInfoMapElm; | |
254 | + } | |
255 | + | |
256 | + /** | |
257 | + * 壁紙情報をXML要素として生成する. | |
258 | + * @param doc XML要素のファクトリ | |
259 | + * @param wallpaperInfo 壁紙情報 | |
260 | + * @return 壁紙情報のXML要素 | |
261 | + */ | |
262 | + public Element writeWallpaper(Document doc, WallpaperInfo wallpaperInfo) { | |
263 | + Element elm = doc.createElementNS(NS, "wallpaperInfo"); | |
264 | + | |
265 | + if (wallpaperInfo == null) { | |
266 | + elm.setAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "xsi:nil", "true"); | |
267 | + | |
268 | + } else { | |
269 | + // タイプ | |
270 | + WallpaperResourceType typ = wallpaperInfo.getType(); | |
271 | + Element typElm = doc.createElementNS(NS, "type"); | |
272 | + typElm.setTextContent(typ.name()); | |
273 | + elm.appendChild(typElm); | |
274 | + | |
275 | + // リソース | |
276 | + String res = wallpaperInfo.getResource(); | |
277 | + Element resElm = doc.createElementNS(NS, "resource"); | |
278 | + if (res == null || res.trim().length() == 0) { | |
279 | + resElm.setAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "xsi:nil", "true"); | |
280 | + } else { | |
281 | + resElm.setTextContent(res); | |
282 | + } | |
283 | + elm.appendChild(resElm); | |
284 | + | |
285 | + // ファイル | |
286 | + File file = wallpaperInfo.getFile(); | |
287 | + Element fileElm = doc.createElementNS(NS, "file"); | |
288 | + if (file == null) { | |
289 | + fileElm.setAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "xsi:nil", "true"); | |
290 | + } else { | |
291 | + fileElm.setTextContent(file.getPath()); | |
292 | + } | |
293 | + elm.appendChild(fileElm); | |
294 | + | |
295 | + // アルファ | |
296 | + float alpha = wallpaperInfo.getAlpha(); | |
297 | + Element alphaElm = doc.createElementNS(NS, "alpha"); | |
298 | + alphaElm.setTextContent(Float.toString(alpha)); | |
299 | + elm.appendChild(alphaElm); | |
300 | + | |
301 | + // 背景色 | |
302 | + Color backgroundColor = wallpaperInfo.getBackgroundColor(); | |
303 | + Element bgColorElm = doc.createElementNS(NS, "backgroundColor"); | |
304 | + bgColorElm.setTextContent("#" + Integer.toHexString(backgroundColor.getRGB() & 0xffffff)); | |
305 | + elm.appendChild(bgColorElm); | |
306 | + } | |
307 | + | |
308 | + return elm; | |
309 | + } | |
310 | + | |
311 | +} |
@@ -3,9 +3,9 @@ | ||
3 | 3 | import java.io.File; |
4 | 4 | import java.io.IOException; |
5 | 5 | import java.io.ObjectInputStream; |
6 | +import java.io.ObjectInputStream.GetField; | |
6 | 7 | import java.io.ObjectStreamClass; |
7 | 8 | import java.io.Serializable; |
8 | -import java.io.ObjectInputStream.GetField; | |
9 | 9 | import java.net.URI; |
10 | 10 | import java.net.URL; |
11 | 11 | import java.util.Map; |
@@ -92,6 +92,12 @@ | ||
92 | 92 | this.characterDataRev = characterDataRev; |
93 | 93 | } |
94 | 94 | |
95 | + /** | |
96 | + * REV情報.<br> | |
97 | + * キャラクターデータが設定されていない場合に使用される.<br> | |
98 | + * (ver0.96以前旧シリアライズデータ互換用)<br> | |
99 | + * @return | |
100 | + */ | |
95 | 101 | public String getCharacterDataRev() { |
96 | 102 | return characterDataRev; |
97 | 103 | } |
@@ -139,6 +145,7 @@ | ||
139 | 145 | |
140 | 146 | /** |
141 | 147 | * 最後に使用したお気に入りの情報.<br> |
148 | + * 一度もプリセットを使ってなければnull.<br> | |
142 | 149 | * ver0.94以前には存在しなかったため、nullになりえます。 |
143 | 150 | * @return |
144 | 151 | */ |
@@ -145,7 +152,14 @@ | ||
145 | 152 | public PartsSet getLastUsePresetParts() { |
146 | 153 | return lastUsePresetParts; |
147 | 154 | } |
148 | - | |
155 | + | |
156 | + /** | |
157 | + /** | |
158 | + * 最後に使用したお気に入りの情報.<br> | |
159 | + * 一度もプリセットを使ってなければnull.<br> | |
160 | + * (ver0.94以前には存在しなかったため、nullになりえます。) | |
161 | + * @param lastUsePresetParts | |
162 | + */ | |
149 | 163 | public void setLastUsePresetParts(PartsSet lastUsePresetParts) { |
150 | 164 | this.lastUsePresetParts = lastUsePresetParts; |
151 | 165 | } |
@@ -1,7 +1,6 @@ | ||
1 | 1 | package charactermanaj.ui; |
2 | 2 | |
3 | -import static java.lang.Math.max; | |
4 | -import static java.lang.Math.min; | |
3 | +import static java.lang.Math.*; | |
5 | 4 | |
6 | 5 | import java.awt.BorderLayout; |
7 | 6 | import java.awt.Color; |
@@ -23,6 +22,7 @@ | ||
23 | 22 | import java.awt.image.BufferedImage; |
24 | 23 | import java.io.File; |
25 | 24 | import java.io.IOException; |
25 | +import java.io.OutputStream; | |
26 | 26 | import java.lang.reflect.InvocationTargetException; |
27 | 27 | import java.util.ArrayList; |
28 | 28 | import java.util.Collections; |
@@ -80,6 +80,7 @@ | ||
80 | 80 | import charactermanaj.model.io.PartsImageDirectoryWatchEvent; |
81 | 81 | import charactermanaj.model.io.PartsImageDirectoryWatchListener; |
82 | 82 | import charactermanaj.model.io.RecentDataPersistent; |
83 | +import charactermanaj.model.io.WorkingSetXMLWriter; | |
83 | 84 | import charactermanaj.ui.ImageSelectPanel.ImageSelectPanelEvent; |
84 | 85 | import charactermanaj.ui.ImageSelectPanel.ImageSelectPanelListener; |
85 | 86 | import charactermanaj.ui.ManageFavoriteDialog.FavoriteManageCallback; |
@@ -1865,6 +1866,17 @@ | ||
1865 | 1866 | UserData workingSetStore = getWorkingSetUserData(false); |
1866 | 1867 | workingSetStore.save(workingSet); |
1867 | 1868 | |
1869 | + // XML形式でのワーキングセットの保存 | |
1870 | + UserDataFactory userDataFactory = UserDataFactory.getInstance(); | |
1871 | + UserData xmlData = userDataFactory.getMangledNamedUserData(characterData.getDocBase(), "workingset.xml"); | |
1872 | + OutputStream outstm = xmlData.getOutputStream(); | |
1873 | + try { | |
1874 | + WorkingSetXMLWriter workingSetXmlWriter = new WorkingSetXMLWriter(); | |
1875 | + workingSetXmlWriter.writeWorkingSet(workingSet, outstm); | |
1876 | + } finally { | |
1877 | + outstm.close(); | |
1878 | + } | |
1879 | + | |
1868 | 1880 | } catch (Exception ex) { |
1869 | 1881 | ErrorMessageHelper.showErrorDialog(this, ex); |
1870 | 1882 | } |
@@ -53,14 +53,25 @@ | ||
53 | 53 | File userDataDir = ConfigurationDirUtilities.getUserDataDir(); |
54 | 54 | |
55 | 55 | if (name != null && name.length() > 0) { |
56 | - int pos = name.lastIndexOf('.'); | |
57 | - if (pos >= 0) { | |
58 | - String ext = name.substring(pos + 1); | |
59 | - if (ext.length() > 0) { | |
60 | - if ("ser".equals(ext)) { | |
61 | - userDataDir = new File(userDataDir, "caches"); | |
62 | - } else { | |
63 | - userDataDir = new File(userDataDir, ext + "s"); | |
56 | + int seppos = name.lastIndexOf('-'); | |
57 | + if (name.endsWith(".xml") && seppos >= 0) { | |
58 | + // 「foo-????.xml」形式の場合は「????」でグループ化する | |
59 | + String groupName = name.substring(seppos + 1, name.length() - 4); | |
60 | + if (groupName.length() > 0) { | |
61 | + userDataDir = new File(userDataDir, groupName); | |
62 | + } | |
63 | + | |
64 | + } else { | |
65 | + // 拡張子によるグループ化 | |
66 | + int pos = name.lastIndexOf('.'); | |
67 | + if (pos >= 0) { | |
68 | + String ext = name.substring(pos + 1); | |
69 | + if (ext.length() > 0) { | |
70 | + if ("ser".equals(ext)) { | |
71 | + userDataDir = new File(userDataDir, "caches"); | |
72 | + } else { | |
73 | + userDataDir = new File(userDataDir, ext + "s"); | |
74 | + } | |
64 | 75 | } |
65 | 76 | } |
66 | 77 | } |