From 5c3f1fc51320232202c5751049c4e89d2a0da123 Mon Sep 17 00:00:00 2001 From: Olyutorskii Date: Sun, 19 May 2013 23:00:55 +0900 Subject: [PATCH] =?utf8?q?=E3=83=91=E3=83=83=E3=82=B1=E3=83=BC=E3=82=B8?= =?utf8?q?=E7=A7=BB=E5=8B=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- .../jp/sfjp/mikutoga/bin/parser/BinParser.java | 145 +++++ .../jp/sfjp/mikutoga/bin/parser/CommonParser.java | 174 +++--- .../jp/sfjp/mikutoga/bin/parser/ParseStage.java | 5 +- .../jp/sfjp/mikutoga/bin/parser/ProxyParser.java | 194 ++++++ .../sfjp/mikutoga/corelib/EmptyProxyFactory.java | 2 +- .../mikutoga/pmd/{PmdLimits.java => PmdConst.java} | 4 +- .../jp/sfjp/mikutoga/pmd/parser/PmdParserBase.java | 14 +- .../jp/sfjp/mikutoga/pmd/parser/PmdParserExt1.java | 12 +- .../jp/sfjp/mikutoga/pmd/parser/PmdParserExt2.java | 8 +- .../jp/sfjp/mikutoga/pmd/parser/PmdParserExt3.java | 6 +- .../mikutoga/vmd/AbstractNumbered.java | 2 +- .../mikutoga/vmd/FrameNumbered.java | 2 +- .../mikutoga/vmd/IllegalVmdDataException.java | 2 +- src/main/java/jp/sfjp/mikutoga/vmd/VmdConst.java | 50 ++ src/main/java/jp/sfjp/mikutoga/vmd/VmdUniq.java | 68 +++ .../mikutoga/vmd/package-info.java | 2 +- .../mikutoga/vmd/parser/VmdBasicHandler.java | 2 +- .../mikutoga/vmd/parser/VmdBasicParser.java | 48 +- .../mikutoga/vmd/parser/VmdCameraHandler.java | 2 +- .../mikutoga/vmd/parser/VmdCameraParser.java | 14 +- .../mikutoga/vmd/parser/VmdLightingHandler.java | 2 +- .../mikutoga/vmd/parser/VmdLightingParser.java | 14 +- .../mikutoga/vmd/parser/VmdParser.java | 51 +- .../mikutoga/vmd/parser/VmdUnifiedHandler.java | 2 +- .../mikutoga/vmd/parser/package-info.java | 2 +- .../java/jp/sourceforge/mikutoga/vmd/VmdConst.java | 123 ---- .../mikutoga/xml/AbstractXmlExporter.java | 664 +++++++++++++++++++++ .../sourceforge/mikutoga/xml/BasicXmlExporter.java | 540 +---------------- .../jp/sourceforge/mikutoga/xml/LocalSchema.java | 163 ----- .../sourceforge/mikutoga/xml/LocalXmlResource.java | 31 + .../sourceforge/mikutoga/xml/ProxyXmlExporter.java | 159 +++++ .../jp/sourceforge/mikutoga/xml/SchemaUtil.java | 151 +++++ .../jp/sourceforge/mikutoga/xml/XmlExporter.java | 319 ++++++++++ .../mikutoga/xml/XmlResourceResolver.java | 15 + src/test/java/sample/vmd/DummyHandler.java | 14 +- src/test/java/sample/vmd/DummyMain.java | 5 +- 37 files changed, 2028 insertions(+), 985 deletions(-) create mode 100644 src/main/java/jp/sfjp/mikutoga/bin/parser/BinParser.java create mode 100644 src/main/java/jp/sfjp/mikutoga/bin/parser/ProxyParser.java rename src/main/java/jp/sfjp/mikutoga/pmd/{PmdLimits.java => PmdConst.java} (97%) rename src/main/java/jp/{sourceforge => sfjp}/mikutoga/vmd/AbstractNumbered.java (95%) rename src/main/java/jp/{sourceforge => sfjp}/mikutoga/vmd/FrameNumbered.java (97%) rename src/main/java/jp/{sourceforge => sfjp}/mikutoga/vmd/IllegalVmdDataException.java (96%) create mode 100644 src/main/java/jp/sfjp/mikutoga/vmd/VmdConst.java create mode 100644 src/main/java/jp/sfjp/mikutoga/vmd/VmdUniq.java rename src/main/java/jp/{sourceforge => sfjp}/mikutoga/vmd/package-info.java (87%) rename src/main/java/jp/{sourceforge => sfjp}/mikutoga/vmd/parser/VmdBasicHandler.java (99%) rename src/main/java/jp/{sourceforge => sfjp}/mikutoga/vmd/parser/VmdBasicParser.java (87%) rename src/main/java/jp/{sourceforge => sfjp}/mikutoga/vmd/parser/VmdCameraHandler.java (99%) rename src/main/java/jp/{sourceforge => sfjp}/mikutoga/vmd/parser/VmdCameraParser.java (94%) rename src/main/java/jp/{sourceforge => sfjp}/mikutoga/vmd/parser/VmdLightingHandler.java (99%) rename src/main/java/jp/{sourceforge => sfjp}/mikutoga/vmd/parser/VmdLightingParser.java (92%) rename src/main/java/jp/{sourceforge => sfjp}/mikutoga/vmd/parser/VmdParser.java (67%) rename src/main/java/jp/{sourceforge => sfjp}/mikutoga/vmd/parser/VmdUnifiedHandler.java (92%) rename src/main/java/jp/{sourceforge => sfjp}/mikutoga/vmd/parser/package-info.java (91%) delete mode 100644 src/main/java/jp/sourceforge/mikutoga/vmd/VmdConst.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/xml/AbstractXmlExporter.java delete mode 100644 src/main/java/jp/sourceforge/mikutoga/xml/LocalSchema.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/xml/LocalXmlResource.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/xml/ProxyXmlExporter.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/xml/SchemaUtil.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/xml/XmlExporter.java diff --git a/pom.xml b/pom.xml index 73470b5..732123b 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ jp.sourceforge.mikutoga togagem - 2.102.3-SNAPSHOT + 2.102.5-SNAPSHOT jar TogaGem diff --git a/src/main/java/jp/sfjp/mikutoga/bin/parser/BinParser.java b/src/main/java/jp/sfjp/mikutoga/bin/parser/BinParser.java new file mode 100644 index 0000000..3ce9f21 --- /dev/null +++ b/src/main/java/jp/sfjp/mikutoga/bin/parser/BinParser.java @@ -0,0 +1,145 @@ +/* + * binary parser interface + * + * License : The MIT License + * Copyright(c) 2013 MikuToga Partners + */ + +package jp.sfjp.mikutoga.bin.parser; + +import java.io.IOException; + +/** + * バイナリパーサの共通インタフェース。 + *

バイト列、各種プリミティブ型値およびエンコードされた文字列を読み込む。 + *

long,double、およびビッグエンディアン形式のデータは未サポート。 + */ +public interface BinParser { + + /** + * 入力ソースの読み込み位置を返す。 + * @return 入力ソースの読み込み位置。単位はbyte。 + */ + long getPosition(); + + /** + * 入力ソースにまだデータが残っているか判定する。 + * @return まだ読み込んでいないデータが残っていればtrue + * @throws IOException IOエラー + */ + boolean hasMore() throws IOException; + + /** + * 入力ソースを読み飛ばす。 + * @param skipLength 読み飛ばすバイト数。 + * @throws IOException IOエラー + * @throws MmdEofException 読み飛ばす途中でストリーム終端に達した。 + * @see java.io.InputStream#skip(long) + */ + void skip(long skipLength) throws IOException, MmdEofException; + + /** + * byte配列を読み込む。 + * @param dst 格納先配列 + * @param off 読み込み開始オフセット + * @param length 読み込みバイト数 + * @throws NullPointerException 配列がnull + * @throws IndexOutOfBoundsException 引数が配列属性と矛盾 + * @throws IOException IOエラー + * @throws MmdEofException 読み込む途中でストリーム終端に達した。 + * @see java.io.InputStream#read(byte[], int, int) + */ + void parseByteArray(byte[] dst, int off, int length) + throws NullPointerException, + IndexOutOfBoundsException, + IOException, + MmdEofException; + + /** + * byte配列を読み込む。 + *

配列要素全ての読み込みが試みられる。 + * @param dst 格納先配列 + * @throws NullPointerException 配列がnull + * @throws IOException IOエラー + * @throws MmdEofException 読み込む途中でストリーム終端に達した。 + * @see java.io.InputStream#read(byte[]) + */ + void parseByteArray(byte[] dst) + throws NullPointerException, IOException, MmdEofException; + + /** + * byte値を読み込む。 + * @return 読み込んだbyte値 + * @throws IOException IOエラー + * @throws MmdEofException 読み込む途中でストリーム終端に達した。 + */ + byte parseByte() throws IOException, MmdEofException; + + /** + * 符号無し値としてbyte値を読み込み、int型に変換して返す。 + *

符号は拡張されない。(0xffは0x000000ffとなる) + * @return 読み込まれた値のint値 + * @throws IOException IOエラー + * @throws MmdEofException 読み込む途中でストリーム終端に達した。 + */ + int parseUByteAsInt() throws IOException, MmdEofException; + + /** + * byte値を読み込み、boolean型に変換して返す。 + *

0x00は偽、それ以外は真と解釈される。 + * @return 読み込まれた値のboolean値 + * @throws IOException IOエラー + * @throws MmdEofException 読み込む途中でストリーム終端に達した。 + */ + boolean parseBoolean() throws IOException, MmdEofException; + + /** + * short値を読み込む。 + *

short値はリトルエンディアンで格納されていると仮定される。 + * @return 読み込んだshort値 + * @throws IOException IOエラー + * @throws MmdEofException 読み込む途中でストリーム終端に達した。 + */ + short parseLeShort() throws IOException, MmdEofException; + + /** + * 符号無し値としてshort値を読み込み、int型に変換して返す。 + *

符号は拡張されない。(0xffffは0x0000ffffとなる) + *

short値はリトルエンディアンで格納されていると仮定される。 + * @return 読み込まれた値のint値 + * @throws IOException IOエラー + * @throws MmdEofException 読み込む途中でストリーム終端に達した。 + */ + int parseLeUShortAsInt() throws IOException, MmdEofException; + + /** + * int値を読み込む。 + *

int値はリトルエンディアンで格納されていると仮定される。 + * @return 読み込んだint値 + * @throws IOException IOエラー + * @throws MmdEofException 読み込む途中でストリーム終端に達した。 + */ + int parseLeInt() throws IOException, MmdEofException; + + /** + * float値を読み込む。 + *

float値はリトルエンディアンで格納されていると仮定される。 + * @return 読み込んだfloat値 + * @throws IOException IOエラー + * @throws MmdEofException 読み込む途中でストリーム終端に達した。 + */ + float parseLeFloat() throws IOException, MmdEofException; + + /** + * 固定バイト長の文字列を読み込む。 + * @param decoder 文字デコーダ + * @param byteLen 読み込む固定バイト長 + * @return 文字列 + * @throws IOException 入力エラー + * @throws MmdEofException 固定長バイト列を読む前に末端に達した。 + * @throws MmdFormatException 文字エンコーディングに関するエラー + */ + String parseString(TextDecoder decoder, int byteLen) + throws IOException, MmdEofException, MmdFormatException; + +} diff --git a/src/main/java/jp/sfjp/mikutoga/bin/parser/CommonParser.java b/src/main/java/jp/sfjp/mikutoga/bin/parser/CommonParser.java index e1cd3fe..ceedad5 100644 --- a/src/main/java/jp/sfjp/mikutoga/bin/parser/CommonParser.java +++ b/src/main/java/jp/sfjp/mikutoga/bin/parser/CommonParser.java @@ -18,11 +18,9 @@ import java.nio.charset.MalformedInputException; import java.nio.charset.UnmappableCharacterException; /** - * 各種バイナリファイルパーサの共通実装。 - *

バイト列、各種プリミティブ型値およびエンコードされた文字列を読み込む。 - *

long,double、およびビッグエンディアン形式のデータは未サポート。 + * 入力ストリームをソースとするバイナリパーサ実装。 */ -public class CommonParser { +public class CommonParser implements BinParser{ private static final String ERRMSG_ILLENC = "illegal character encoding"; @@ -77,19 +75,21 @@ public class CommonParser { /** - * 入力ソースの読み込み位置を返す。 - * @return 入力ソースの読み込み位置。単位はbyte。 + * {@inheritDoc} + * @return {@inheritDoc} */ - protected long getPosition(){ + @Override + public long getPosition(){ long result = this.position; return result; } /** - * 入力ソースにまだデータが残っているか判定する。 - * @return まだ読み込んでいないデータが残っていればtrue - * @throws IOException IOエラー + * {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} */ + @Override public boolean hasMore() throws IOException{ int bVal; @@ -109,13 +109,13 @@ public class CommonParser { } /** - * 入力ソースを読み飛ばす。 - * @param skipLength 読み飛ばすバイト数。 - * @throws IOException IOエラー - * @throws MmdEofException 読み飛ばす途中でストリーム終端に達した。 - * @see InputStream#skip(long) + * {@inheritDoc} + * @param skipLength {@inheritDoc} + * @throws IOException {@inheritDoc} + * @throws MmdEofException {@inheritDoc} */ - protected void skip(long skipLength) + @Override + public void skip(long skipLength) throws IOException, MmdEofException { long remain = skipLength; @@ -132,20 +132,20 @@ public class CommonParser { } /** - * byte配列を読み込む。 - * @param dst 格納先配列 - * @param off 読み込み開始オフセット - * @param length 読み込みバイト数 - * @throws IOException IOエラー - * @throws NullPointerException 配列がnull - * @throws IndexOutOfBoundsException 引数が配列属性と矛盾 - * @throws MmdEofException 読み込む途中でストリーム終端に達した。 - * @see InputStream#read(byte[], int, int) + * {@inheritDoc} + * @param dst {@inheritDoc} + * @param off {@inheritDoc} + * @param length {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IndexOutOfBoundsException {@inheritDoc} + * @throws IOException {@inheritDoc} + * @throws MmdEofException {@inheritDoc} */ - protected void parseByteArray(byte[] dst, int off, int length) - throws IOException, - NullPointerException, + @Override + public void parseByteArray(byte[] dst, int off, int length) + throws NullPointerException, IndexOutOfBoundsException, + IOException, MmdEofException { int remain = length; int offset = off; @@ -164,16 +164,15 @@ public class CommonParser { } /** - * byte配列を読み込む。 - *

配列要素全ての読み込みが試みられる。 - * @param dst 格納先配列 - * @throws IOException IOエラー - * @throws NullPointerException 配列がnull - * @throws MmdEofException 読み込む途中でストリーム終端に達した。 - * @see InputStream#read(byte[]) + * {@inheritDoc} + * @param dst {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IOException {@inheritDoc} + * @throws MmdEofException {@inheritDoc} */ - protected void parseByteArray(byte[] dst) - throws IOException, NullPointerException, MmdEofException{ + @Override + public void parseByteArray(byte[] dst) + throws NullPointerException, IOException, MmdEofException{ parseByteArray(dst, 0, dst.length); return; } @@ -191,12 +190,13 @@ public class CommonParser { } /** - * byte値を読み込む。 - * @return 読み込んだbyte値 - * @throws IOException IOエラー - * @throws MmdEofException 読み込む途中でストリーム終端に達した。 + * {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + * @throws MmdEofException {@inheritDoc} */ - protected byte parseByte() + @Override + public byte parseByte() throws IOException, MmdEofException{ int bData = this.is.read(); if(bData < 0){ @@ -210,25 +210,25 @@ public class CommonParser { } /** - * 符号無し値としてbyte値を読み込み、int型に変換して返す。 - *

符号は拡張されない。(0xffは0x000000ffとなる) - * @return 読み込まれた値のint値 - * @throws IOException IOエラー - * @throws MmdEofException 読み込む途中でストリーム終端に達した。 + * {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + * @throws MmdEofException {@inheritDoc} */ - protected int parseUByteAsInt() + @Override + public int parseUByteAsInt() throws IOException, MmdEofException{ return parseByte() & MASK_8BIT; } /** - * byte値を読み込み、boolean型に変換して返す。 - *

0x00は偽、それ以外は真と解釈される。 - * @return 読み込まれた値のboolean値 - * @throws IOException IOエラー - * @throws MmdEofException 読み込む途中でストリーム終端に達した。 + * {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + * @throws MmdEofException {@inheritDoc} */ - protected boolean parseBoolean() + @Override + public boolean parseBoolean() throws IOException, MmdEofException{ byte result = parseByte(); if(result == 0x00) return false; @@ -236,13 +236,13 @@ public class CommonParser { } /** - * short値を読み込む。 - *

short値はリトルエンディアンで格納されていると仮定される。 - * @return 読み込んだshort値 - * @throws IOException IOエラー - * @throws MmdEofException 読み込む途中でストリーム終端に達した。 + * {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + * @throws MmdEofException {@inheritDoc} */ - protected short parseLeShort() + @Override + public short parseLeShort() throws IOException, MmdEofException{ fillBuffer(BYTES_SHORT); short result = this.leBuf.getShort(0); @@ -250,26 +250,25 @@ public class CommonParser { } /** - * 符号無し値としてshort値を読み込み、int型に変換して返す。 - *

符号は拡張されない。(0xffffは0x0000ffffとなる) - *

short値はリトルエンディアンで格納されていると仮定される。 - * @return 読み込まれた値のint値 - * @throws IOException IOエラー - * @throws MmdEofException 読み込む途中でストリーム終端に達した。 + * {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + * @throws MmdEofException {@inheritDoc} */ - protected int parseLeUShortAsInt() + @Override + public int parseLeUShortAsInt() throws IOException, MmdEofException{ return parseLeShort() & MASK_16BIT; } /** - * int値を読み込む。 - *

int値はリトルエンディアンで格納されていると仮定される。 - * @return 読み込んだint値 - * @throws IOException IOエラー - * @throws MmdEofException 読み込む途中でストリーム終端に達した。 + * {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + * @throws MmdEofException {@inheritDoc} */ - protected int parseLeInt() + @Override + public int parseLeInt() throws IOException, MmdEofException{ fillBuffer(BYTES_INT); int result = this.leBuf.getInt(0); @@ -277,13 +276,13 @@ public class CommonParser { } /** - * float値を読み込む。 - *

float値はリトルエンディアンで格納されていると仮定される。 - * @return 読み込んだfloat値 - * @throws IOException IOエラー - * @throws MmdEofException 読み込む途中でストリーム終端に達した。 + * {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + * @throws MmdEofException {@inheritDoc} */ - protected float parseLeFloat() + @Override + public float parseLeFloat() throws IOException, MmdEofException{ fillBuffer(BYTES_FLOAT); float result = this.leBuf.getFloat(0); @@ -291,15 +290,16 @@ public class CommonParser { } /** - * 固定バイト長の文字列を読み込む。 - * @param decoder 文字デコーダ - * @param byteLen 読み込む固定バイト長 - * @return 文字列 - * @throws IOException 入力エラー - * @throws MmdEofException 固定長バイト列を読む前に末端に達した。 - * @throws MmdFormatException 文字エンコーディングに関するエラー + * {@inheritDoc} + * @param decoder {@inheritDoc} + * @param byteLen {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + * @throws MmdEofException {@inheritDoc} + * @throws MmdFormatException {@inheritDoc} */ - protected String parseString(TextDecoder decoder, int byteLen) + @Override + public String parseString(TextDecoder decoder, int byteLen) throws IOException, MmdEofException, MmdFormatException { if(this.btextBuf == null || this.btextBuf.capacity() < byteLen){ this.btextBuf = ByteBuffer.allocate(byteLen); diff --git a/src/main/java/jp/sfjp/mikutoga/bin/parser/ParseStage.java b/src/main/java/jp/sfjp/mikutoga/bin/parser/ParseStage.java index 1198b7d..638422a 100644 --- a/src/main/java/jp/sfjp/mikutoga/bin/parser/ParseStage.java +++ b/src/main/java/jp/sfjp/mikutoga/bin/parser/ParseStage.java @@ -18,8 +18,10 @@ public class ParseStage { private static final AtomicInteger LAST_NO = new AtomicInteger(0); + /** 全インスタンスに割り振られたユニークな番号。変更不可。 */ - public final int no; + private final int no; + /** * コンストラクタ。 @@ -31,6 +33,7 @@ public class ParseStage { return; } + /** * 各インスタンスに割り当てられたユニークな通し番号を返す。 * @return 通し番号 diff --git a/src/main/java/jp/sfjp/mikutoga/bin/parser/ProxyParser.java b/src/main/java/jp/sfjp/mikutoga/bin/parser/ProxyParser.java new file mode 100644 index 0000000..578f768 --- /dev/null +++ b/src/main/java/jp/sfjp/mikutoga/bin/parser/ProxyParser.java @@ -0,0 +1,194 @@ +/* + * parser proxy + * + * License : The MIT License + * Copyright(c) 2013 MikuToga Partners + */ + +package jp.sfjp.mikutoga.bin.parser; + +import java.io.IOException; + +/** + * 委譲パーサ。 + *

別のパーサにパース処理を委譲する。 + */ +public class ProxyParser implements BinParser{ + + private final BinParser delegate; + + + /** + * コンストラクタ。 + * @param delegate 委譲先パーサ + * @throws NullPointerException 引数がnull + */ + public ProxyParser(BinParser delegate) throws NullPointerException{ + super(); + + if(delegate == null) throw new NullPointerException(); + this.delegate = delegate; + + return; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public long getPosition() { + return this.delegate.getPosition(); + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public boolean hasMore() throws IOException { + return this.delegate.hasMore(); + } + + /** + * {@inheritDoc} + * @param skipLength {@inheritDoc} + * @throws IOException {@inheritDoc} + * @throws MmdEofException {@inheritDoc} + */ + @Override + public void skip(long skipLength) throws IOException, MmdEofException { + this.delegate.skip(skipLength); + return; + } + + /** + * {@inheritDoc} + * @param dst {@inheritDoc} + * @param off {@inheritDoc} + * @param length {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IndexOutOfBoundsException {@inheritDoc} + * @throws IOException {@inheritDoc} + * @throws MmdEofException {@inheritDoc} + */ + @Override + public void parseByteArray(byte[] dst, int off, int length) + throws NullPointerException, + IndexOutOfBoundsException, + IOException, + MmdEofException { + this.delegate.parseByteArray(dst, off, length); + return; + } + + /** + * {@inheritDoc} + * @param dst {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IOException {@inheritDoc} + * @throws MmdEofException {@inheritDoc} + */ + @Override + public void parseByteArray(byte[] dst) + throws NullPointerException, + IOException, + MmdEofException { + this.delegate.parseByteArray(dst); + return; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + * @throws MmdEofException {@inheritDoc} + */ + @Override + public byte parseByte() throws IOException, MmdEofException { + return this.delegate.parseByte(); + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + * @throws MmdEofException {@inheritDoc} + */ + @Override + public int parseUByteAsInt() throws IOException, MmdEofException { + return this.delegate.parseUByteAsInt(); + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + * @throws MmdEofException {@inheritDoc} + */ + @Override + public boolean parseBoolean() throws IOException, MmdEofException { + return this.delegate.parseBoolean(); + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + * @throws MmdEofException {@inheritDoc} + */ + @Override + public short parseLeShort() throws IOException, MmdEofException { + return this.delegate.parseLeShort(); + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + * @throws MmdEofException {@inheritDoc} + */ + @Override + public int parseLeUShortAsInt() throws IOException, MmdEofException { + return this.delegate.parseLeUShortAsInt(); + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + * @throws MmdEofException {@inheritDoc} + */ + @Override + public int parseLeInt() throws IOException, MmdEofException { + return this.delegate.parseLeInt(); + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + * @throws MmdEofException {@inheritDoc} + */ + @Override + public float parseLeFloat() throws IOException, MmdEofException { + return this.delegate.parseLeFloat(); + } + + /** + * {@inheritDoc} + * @param decoder {@inheritDoc} + * @param byteLen {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + * @throws MmdEofException {@inheritDoc} + * @throws MmdFormatException {@inheritDoc} + */ + @Override + public String parseString(TextDecoder decoder, int byteLen) + throws IOException, MmdEofException, MmdFormatException { + return this.delegate.parseString(decoder, byteLen); + } + +} diff --git a/src/main/java/jp/sfjp/mikutoga/corelib/EmptyProxyFactory.java b/src/main/java/jp/sfjp/mikutoga/corelib/EmptyProxyFactory.java index 360074a..bbfba54 100644 --- a/src/main/java/jp/sfjp/mikutoga/corelib/EmptyProxyFactory.java +++ b/src/main/java/jp/sfjp/mikutoga/corelib/EmptyProxyFactory.java @@ -17,7 +17,7 @@ import java.lang.reflect.Proxy; public final class EmptyProxyFactory { /** 何もせず何も返さないInvoker。 */ - public static InvocationHandler NOTHING_INVOKER = new Nothing(); + public static final InvocationHandler NOTHING_INVOKER = new Nothing(); /** diff --git a/src/main/java/jp/sfjp/mikutoga/pmd/PmdLimits.java b/src/main/java/jp/sfjp/mikutoga/pmd/PmdConst.java similarity index 97% rename from src/main/java/jp/sfjp/mikutoga/pmd/PmdLimits.java rename to src/main/java/jp/sfjp/mikutoga/pmd/PmdConst.java index e97d23f..07f2b44 100644 --- a/src/main/java/jp/sfjp/mikutoga/pmd/PmdLimits.java +++ b/src/main/java/jp/sfjp/mikutoga/pmd/PmdConst.java @@ -10,7 +10,7 @@ package jp.sfjp.mikutoga.pmd; /** * PMDファイルフォーマットの各種リミット値その他定数。 */ -public final class PmdLimits { +public final class PmdConst { /** モデル名最大長。バイト単位。 */ public static final int MAXBYTES_MODELNAME = 20; @@ -57,7 +57,7 @@ public final class PmdLimits { /** * 隠しコンストラクタ。 */ - private PmdLimits(){ + private PmdConst(){ super(); assert false; throw new AssertionError(); diff --git a/src/main/java/jp/sfjp/mikutoga/pmd/parser/PmdParserBase.java b/src/main/java/jp/sfjp/mikutoga/pmd/parser/PmdParserBase.java index e991ad0..ca41f78 100644 --- a/src/main/java/jp/sfjp/mikutoga/pmd/parser/PmdParserBase.java +++ b/src/main/java/jp/sfjp/mikutoga/pmd/parser/PmdParserBase.java @@ -15,7 +15,7 @@ import jp.sfjp.mikutoga.bin.parser.CommonParser; import jp.sfjp.mikutoga.bin.parser.MmdEofException; import jp.sfjp.mikutoga.bin.parser.MmdFormatException; import jp.sfjp.mikutoga.bin.parser.TextDecoder; -import jp.sfjp.mikutoga.pmd.PmdLimits; +import jp.sfjp.mikutoga.pmd.PmdConst; import jp.sfjp.mikutoga.pmd.ShadingUtil; /** @@ -257,9 +257,9 @@ public class PmdParserBase extends CommonParser { } String modelName = - parsePmdText(PmdLimits.MAXBYTES_MODELNAME); + parsePmdText(PmdConst.MAXBYTES_MODELNAME); String description = - parsePmdText(PmdLimits.MAXBYTES_MODELDESC); + parsePmdText(PmdConst.MAXBYTES_MODELDESC); description = description.replace(CRLF, LF); this.basicHandler.pmdHeaderInfo(header); @@ -355,7 +355,7 @@ public class PmdParserBase extends CommonParser { int surfaceCount = parseLeInt(); String shadingFile = - parsePmdText(PmdLimits.MAXBYTES_TEXTUREFILENAME); + parsePmdText(PmdConst.MAXBYTES_TEXTUREFILENAME); String[] splitted = ShadingUtil.splitShadingFileInfo(shadingFile); String textureFile = splitted[0]; String sphereFile = splitted[1]; @@ -418,7 +418,7 @@ public class PmdParserBase extends CommonParser { for(int ct = 0; ct < this.boneCount; ct++){ String boneName = - parsePmdText(PmdLimits.MAXBYTES_BONENAME); + parsePmdText(PmdConst.MAXBYTES_BONENAME); int parentId = parseLeUShortAsInt(); int tailId = parseLeUShortAsInt(); byte boneKind = parseByte(); @@ -506,7 +506,7 @@ public class PmdParserBase extends CommonParser { for(int ct = 0; ct < this.morphCount; ct++){ String morphName = - parsePmdText(PmdLimits.MAXBYTES_MORPHNAME); + parsePmdText(PmdConst.MAXBYTES_MORPHNAME); int vertexCount = parseLeInt(); byte morphType = parseByte(); @@ -586,7 +586,7 @@ public class PmdParserBase extends CommonParser { for(int ct = 0; ct < this.boneGroupCount; ct++){ String groupName = - parsePmdText(PmdLimits.MAXBYTES_BONEGROUPNAME); + parsePmdText(PmdConst.MAXBYTES_BONEGROUPNAME); groupName = chopLastLF(groupName); this.boneHandler.pmdBoneGroupInfo(groupName); diff --git a/src/main/java/jp/sfjp/mikutoga/pmd/parser/PmdParserExt1.java b/src/main/java/jp/sfjp/mikutoga/pmd/parser/PmdParserExt1.java index fbab5a8..680c83f 100644 --- a/src/main/java/jp/sfjp/mikutoga/pmd/parser/PmdParserExt1.java +++ b/src/main/java/jp/sfjp/mikutoga/pmd/parser/PmdParserExt1.java @@ -10,7 +10,7 @@ package jp.sfjp.mikutoga.pmd.parser; import java.io.IOException; import java.io.InputStream; import jp.sfjp.mikutoga.bin.parser.MmdFormatException; -import jp.sfjp.mikutoga.pmd.PmdLimits; +import jp.sfjp.mikutoga.pmd.PmdConst; /** * PMDモデルファイルのパーサ拡張その1。 @@ -78,9 +78,9 @@ public class PmdParserExt1 extends PmdParserBase { if( ! this.hasEnglishInfo ) return; String modelName = - parsePmdText(PmdLimits.MAXBYTES_MODELNAME); + parsePmdText(PmdConst.MAXBYTES_MODELNAME); String description = - parsePmdText(PmdLimits.MAXBYTES_MODELDESC); + parsePmdText(PmdConst.MAXBYTES_MODELDESC); description = description.replace(CRLF, LF); this.engHandler.pmdEngModelInfo(modelName, description); @@ -101,7 +101,7 @@ public class PmdParserExt1 extends PmdParserBase { for(int ct = 0; ct < boneNum; ct++){ String boneName = - parsePmdText(PmdLimits.MAXBYTES_BONENAME); + parsePmdText(PmdConst.MAXBYTES_BONENAME); this.engHandler.pmdEngBoneInfo(boneName); this.engHandler.loopNext(PmdEngHandler.ENGBONE_LIST); @@ -125,7 +125,7 @@ public class PmdParserExt1 extends PmdParserBase { for(int ct = 0; ct < morphNum; ct++){ String morphName = - parsePmdText(PmdLimits.MAXBYTES_MORPHNAME); + parsePmdText(PmdConst.MAXBYTES_MORPHNAME); this.engHandler.pmdEngMorphInfo(morphName); this.engHandler.loopNext(PmdEngHandler.ENGMORPH_LIST); @@ -149,7 +149,7 @@ public class PmdParserExt1 extends PmdParserBase { for(int ct = 0; ct < groupNum; ct++){ String boneGroupName = - parsePmdText(PmdLimits.MAXBYTES_BONEGROUPNAME); + parsePmdText(PmdConst.MAXBYTES_BONEGROUPNAME); this.engHandler.pmdEngBoneGroupInfo(boneGroupName); this.engHandler.loopNext(PmdEngHandler.ENGBONEGROUP_LIST); diff --git a/src/main/java/jp/sfjp/mikutoga/pmd/parser/PmdParserExt2.java b/src/main/java/jp/sfjp/mikutoga/pmd/parser/PmdParserExt2.java index 3e6ce5c..3e9dd3c 100644 --- a/src/main/java/jp/sfjp/mikutoga/pmd/parser/PmdParserExt2.java +++ b/src/main/java/jp/sfjp/mikutoga/pmd/parser/PmdParserExt2.java @@ -10,7 +10,7 @@ package jp.sfjp.mikutoga.pmd.parser; import java.io.IOException; import java.io.InputStream; import jp.sfjp.mikutoga.bin.parser.MmdFormatException; -import jp.sfjp.mikutoga.pmd.PmdLimits; +import jp.sfjp.mikutoga.pmd.PmdConst; /** * PMDモデルファイルのパーサ拡張その2。 @@ -66,11 +66,11 @@ public class PmdParserExt2 extends PmdParserExt1 { */ private void parseToonName() throws IOException, MmdFormatException{ this.toonHandler.loopStart(PmdToonHandler.TOON_LIST, - PmdLimits.TOON_FIXEDNUM ); + PmdConst.TOON_FIXEDNUM ); - for(int ct = 0; ct < PmdLimits.TOON_FIXEDNUM; ct++){ + for(int ct = 0; ct < PmdConst.TOON_FIXEDNUM; ct++){ String toonName = - parsePmdText(PmdLimits.MAXBYTES_TOONFILENAME); + parsePmdText(PmdConst.MAXBYTES_TOONFILENAME); this.toonHandler.pmdToonFileInfo(toonName); this.toonHandler.loopNext(PmdToonHandler.TOON_LIST); diff --git a/src/main/java/jp/sfjp/mikutoga/pmd/parser/PmdParserExt3.java b/src/main/java/jp/sfjp/mikutoga/pmd/parser/PmdParserExt3.java index 555f185..eb4f6f4 100644 --- a/src/main/java/jp/sfjp/mikutoga/pmd/parser/PmdParserExt3.java +++ b/src/main/java/jp/sfjp/mikutoga/pmd/parser/PmdParserExt3.java @@ -10,7 +10,7 @@ package jp.sfjp.mikutoga.pmd.parser; import java.io.IOException; import java.io.InputStream; import jp.sfjp.mikutoga.bin.parser.MmdFormatException; -import jp.sfjp.mikutoga.pmd.PmdLimits; +import jp.sfjp.mikutoga.pmd.PmdConst; /** * PMDモデルファイルのパーサ拡張その3。 @@ -86,7 +86,7 @@ public class PmdParserExt3 extends PmdParserExt2 { for(int ct = 0; ct < rigidNum; ct++){ String rigidName = - parsePmdText(PmdLimits.MAXBYTES_RIGIDNAME); + parsePmdText(PmdConst.MAXBYTES_RIGIDNAME); this.rigidHandler.pmdRigidName(rigidName); int linkedBoneId = parseLeUShortAsInt(); @@ -168,7 +168,7 @@ public class PmdParserExt3 extends PmdParserExt2 { for(int ct = 0; ct < jointNum; ct++){ String jointName = - parsePmdText(PmdLimits.MAXBYTES_JOINTNAME); + parsePmdText(PmdConst.MAXBYTES_JOINTNAME); this.jointHandler.pmdJointName(jointName); int rigidIdA = parseLeInt(); diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/AbstractNumbered.java b/src/main/java/jp/sfjp/mikutoga/vmd/AbstractNumbered.java similarity index 95% rename from src/main/java/jp/sourceforge/mikutoga/vmd/AbstractNumbered.java rename to src/main/java/jp/sfjp/mikutoga/vmd/AbstractNumbered.java index 23935cd..db0d8ad 100644 --- a/src/main/java/jp/sourceforge/mikutoga/vmd/AbstractNumbered.java +++ b/src/main/java/jp/sfjp/mikutoga/vmd/AbstractNumbered.java @@ -5,7 +5,7 @@ * Copyright(c) 2011 MikuToga Partners */ -package jp.sourceforge.mikutoga.vmd; +package jp.sfjp.mikutoga.vmd; /** * FrameNumberedの抽象実装クラス。 diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/FrameNumbered.java b/src/main/java/jp/sfjp/mikutoga/vmd/FrameNumbered.java similarity index 97% rename from src/main/java/jp/sourceforge/mikutoga/vmd/FrameNumbered.java rename to src/main/java/jp/sfjp/mikutoga/vmd/FrameNumbered.java index 7e7fabc..0adc470 100644 --- a/src/main/java/jp/sourceforge/mikutoga/vmd/FrameNumbered.java +++ b/src/main/java/jp/sfjp/mikutoga/vmd/FrameNumbered.java @@ -5,7 +5,7 @@ * Copyright(c) 2011 MikuToga Partners */ -package jp.sourceforge.mikutoga.vmd; +package jp.sfjp.mikutoga.vmd; import java.util.Comparator; diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/IllegalVmdDataException.java b/src/main/java/jp/sfjp/mikutoga/vmd/IllegalVmdDataException.java similarity index 96% rename from src/main/java/jp/sourceforge/mikutoga/vmd/IllegalVmdDataException.java rename to src/main/java/jp/sfjp/mikutoga/vmd/IllegalVmdDataException.java index d1b4caa..429571e 100644 --- a/src/main/java/jp/sourceforge/mikutoga/vmd/IllegalVmdDataException.java +++ b/src/main/java/jp/sfjp/mikutoga/vmd/IllegalVmdDataException.java @@ -5,7 +5,7 @@ * Copyright(c) 2011 MikuToga Partners */ -package jp.sourceforge.mikutoga.vmd; +package jp.sfjp.mikutoga.vmd; /** * VMDモーションデータの不備を発見した場合の例外。 diff --git a/src/main/java/jp/sfjp/mikutoga/vmd/VmdConst.java b/src/main/java/jp/sfjp/mikutoga/vmd/VmdConst.java new file mode 100644 index 0000000..f60a7a4 --- /dev/null +++ b/src/main/java/jp/sfjp/mikutoga/vmd/VmdConst.java @@ -0,0 +1,50 @@ +/* + * VMD constant data + * + * License : The MIT License + * Copyright(c) 2011 MikuToga Partners + */ + +package jp.sfjp.mikutoga.vmd; + +/** + * VMDファイルフォーマットの各種定数。 + */ +public final class VmdConst { + + /** + * VMDファイルヘッダ長。バイト単位。 + */ + public static final int HEADER_LENGTH = 30; + + /** + * ファイルヘッダ部先頭のASCIIコード相当部。 + */ + public static final String MAGIC_TXT = "Vocaloid Motion Data 0002"; + + /** + * モデル名最大長。バイト単位。 + */ + public static final int MODELNAME_MAX = 20; + + /** + * ボーン名最大長。バイト単位。 + */ + public static final int BONENAME_MAX = 15; + + /** + * モーフ名最大長。バイト単位。 + */ + public static final int MORPHNAME_MAX = 15; + + + /** + * 隠しコンストラクタ。 + */ + private VmdConst(){ + super(); + assert false; + throw new AssertionError(); + } + +} diff --git a/src/main/java/jp/sfjp/mikutoga/vmd/VmdUniq.java b/src/main/java/jp/sfjp/mikutoga/vmd/VmdUniq.java new file mode 100644 index 0000000..44faede --- /dev/null +++ b/src/main/java/jp/sfjp/mikutoga/vmd/VmdUniq.java @@ -0,0 +1,68 @@ +/* + * VMD special unique names + * + * License : The MIT License + * Copyright(c) 2013 MikuToga Partners + */ + +package jp.sfjp.mikutoga.vmd; + +/** + * 各種特殊名に関する定義。 + */ +public final class VmdUniq { + + /** + * 便宜上割り振られる特殊なモーフ名。 + */ + public static final String MORPHNAME_BASE = "base"; + + /** + * カメラもしくはライティングデータに便宜上割り当てられるモデル名。 + */ + public static final String MODELNAME_STAGEACT = + "\u30ab\u30e1\u30e9\u30fb\u7167\u660e"; // "カメラ・照明"; + + static{ + assert "カメラ・照明".equals(VmdUniq.MODELNAME_STAGEACT); + } + + + /** + * 隠しコンストラクタ。 + */ + private VmdUniq(){ + assert false; + throw new AssertionError(); + } + + + /** + * 特殊なモーフ名「base」か否か判定する。 + * @param morphName モーフ名 + * @return モーフ名が「base」ならtrue + */ + public static boolean isBaseMorphName(String morphName) { + if(MORPHNAME_BASE.equals(morphName)){ + return true; + } + return false; + } + + /** + * カメラやライティングなどのステージ演出データの可能性があるか、 + * モデル名から推測する。 + *

モデル名が「カメラ・照明」である場合、 + * そのモーションファイルはほぼ + * カメラ・ライティング用ステージ演出データであると推測される。 + * @param modelName モデル名 + * @return モデル名にカメラもしくはライティングの可能性があるならtrue + */ + public static boolean isStageActName(String modelName) { + if(MODELNAME_STAGEACT.equals(modelName)){ + return true; + } + return false; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/package-info.java b/src/main/java/jp/sfjp/mikutoga/vmd/package-info.java similarity index 87% rename from src/main/java/jp/sourceforge/mikutoga/vmd/package-info.java rename to src/main/java/jp/sfjp/mikutoga/vmd/package-info.java index 3aaa970..5fd9fb2 100644 --- a/src/main/java/jp/sourceforge/mikutoga/vmd/package-info.java +++ b/src/main/java/jp/sfjp/mikutoga/vmd/package-info.java @@ -10,6 +10,6 @@ * VMDファイル(*.vmd)操作関連の共通資産。 */ -package jp.sourceforge.mikutoga.vmd; +package jp.sfjp.mikutoga.vmd; /* EOF */ diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdBasicHandler.java b/src/main/java/jp/sfjp/mikutoga/vmd/parser/VmdBasicHandler.java similarity index 99% rename from src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdBasicHandler.java rename to src/main/java/jp/sfjp/mikutoga/vmd/parser/VmdBasicHandler.java index 1c97284..08b8ba0 100644 --- a/src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdBasicHandler.java +++ b/src/main/java/jp/sfjp/mikutoga/vmd/parser/VmdBasicHandler.java @@ -5,7 +5,7 @@ * Copyright(c) 2011 MikuToga Partners */ -package jp.sourceforge.mikutoga.vmd.parser; +package jp.sfjp.mikutoga.vmd.parser; import jp.sfjp.mikutoga.bin.parser.LoopHandler; import jp.sfjp.mikutoga.bin.parser.MmdFormatException; diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdBasicParser.java b/src/main/java/jp/sfjp/mikutoga/vmd/parser/VmdBasicParser.java similarity index 87% rename from src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdBasicParser.java rename to src/main/java/jp/sfjp/mikutoga/vmd/parser/VmdBasicParser.java index c65f109..5316461 100644 --- a/src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdBasicParser.java +++ b/src/main/java/jp/sfjp/mikutoga/vmd/parser/VmdBasicParser.java @@ -5,22 +5,23 @@ * Copyright(c) 2011 MikuToga Partners */ -package jp.sourceforge.mikutoga.vmd.parser; +package jp.sfjp.mikutoga.vmd.parser; import java.io.IOException; -import java.io.InputStream; import java.nio.charset.Charset; -import jp.sfjp.mikutoga.bin.parser.CommonParser; +import jp.sfjp.mikutoga.bin.parser.BinParser; import jp.sfjp.mikutoga.bin.parser.MmdEofException; import jp.sfjp.mikutoga.bin.parser.MmdFormatException; +import jp.sfjp.mikutoga.bin.parser.ProxyParser; import jp.sfjp.mikutoga.bin.parser.TextDecoder; -import jp.sourceforge.mikutoga.vmd.VmdConst; +import jp.sfjp.mikutoga.vmd.VmdConst; +import jp.sfjp.mikutoga.vmd.VmdUniq; /** * VMDモーションファイルの基本部パーサ。 *

ボーンのモーション情報およびモーフモーション情報のパース処理を含む。 */ -class VmdBasicParser extends CommonParser{ +class VmdBasicParser extends ProxyParser{ /** * VMDで用いられる文字エンコーディング(windows-31j)。 @@ -28,6 +29,7 @@ class VmdBasicParser extends CommonParser{ * デコード結果はUCS-2集合に収まるはず。 */ public static final Charset CS_WIN31J = Charset.forName("windows-31j"); + private static final Charset CS_ASCII = Charset.forName("US-ASCII"); private static final int BZ_SIZE = 4; // 4byte Bezier parameter private static final int BZXYZR_SIZE = BZ_SIZE * 4; // XYZR Bezier @@ -37,6 +39,8 @@ class VmdBasicParser extends CommonParser{ private static final String ERRMSG_INVINTPLT = "there is potential inconsistency in motion interpolation data. " +"(Strict-mode)"; + private static final String ERRMSG_UK_HEADER = + "unknown VMD-header type"; private final TextDecoder decoderWin31j = new TextDecoder(CS_WIN31J); @@ -46,15 +50,15 @@ class VmdBasicParser extends CommonParser{ private VmdBasicHandler handler = VmdUnifiedHandler.EMPTY; private boolean hasStageActName = false; - private boolean strictMode = true; + private boolean redundantCheck = false; /** * コンストラクタ。 - * @param source 入力ソース + * @param parser 委譲先パーサ */ - VmdBasicParser(InputStream source){ - super(source); + VmdBasicParser(BinParser parser){ + super(parser); this.decoderWin31j.setZeroChopMode(true); return; } @@ -83,14 +87,12 @@ class VmdBasicParser extends CommonParser{ } /** - * 厳密なパース(Strict-mode)を行うか否か設定する。 - * デフォルトではStrict-modeはオン。 - *

Strict-mode下では、 - * ボーンモーションの冗長な補間情報の一貫性チェックが行わる。 - * @param mode Strict-modeに設定したければtrue + * ボーンモーション補間情報冗長部のチェックを行うか否か設定する。 + * デフォルトではチェックを行わない。 + * @param mode チェックさせたければtrue */ - void setStrictMode(boolean mode){ - this.strictMode = mode; + void setRedundantCheck(boolean mode){ + this.redundantCheck = mode; return; } @@ -107,7 +109,7 @@ class VmdBasicParser extends CommonParser{ * @throws MmdEofException 読み込む途中でストリーム終端に達した。 * @throws MmdFormatException 不正な文字エンコーディングが検出された。 */ - protected String parseVmdText(int byteLen) + private String parseVmdText(int byteLen) throws IOException, MmdEofException, MmdFormatException { @@ -140,8 +142,11 @@ class VmdBasicParser extends CommonParser{ byte[] header = new byte[VmdConst.HEADER_LENGTH]; parseByteArray(header); - if( ! VmdConst.startsWithMagic(header) ){ - throw new MmdFormatException("unknown VMD-header type"); + byte[] magic = (VmdConst.MAGIC_TXT + '\0').getBytes(CS_ASCII); + for(int idx = 0; idx < magic.length; idx++){ + if(header.length <= idx || header[idx] != magic[idx]){ + throw new MmdFormatException(ERRMSG_UK_HEADER); + } } this.handler.vmdHeaderInfo(header); @@ -157,7 +162,7 @@ class VmdBasicParser extends CommonParser{ private void parseVmdModelName() throws IOException, MmdFormatException{ String modelName = parseVmdText(VmdConst.MODELNAME_MAX); - if(VmdConst.isStageActName(modelName)){ + if(VmdUniq.isStageActName(modelName)){ this.hasStageActName = true; } @@ -213,7 +218,7 @@ class VmdBasicParser extends CommonParser{ throws IOException, MmdFormatException{ parseByteArray(this.motionIntplt); - if(this.strictMode){ + if(this.redundantCheck){ checkIntpltStrict(); } @@ -251,6 +256,7 @@ class VmdBasicParser extends CommonParser{ /** * 補間情報の冗長箇所の整合性チェックを行う。 + *

※ MMDの版数によって微妙に詳細が異なる場合がある。 * @throws MmdFormatException 冗長箇所の不整合を検出した。 */ private void checkIntpltStrict() throws MmdFormatException{ diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdCameraHandler.java b/src/main/java/jp/sfjp/mikutoga/vmd/parser/VmdCameraHandler.java similarity index 99% rename from src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdCameraHandler.java rename to src/main/java/jp/sfjp/mikutoga/vmd/parser/VmdCameraHandler.java index 712578a..7fbef1f 100644 --- a/src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdCameraHandler.java +++ b/src/main/java/jp/sfjp/mikutoga/vmd/parser/VmdCameraHandler.java @@ -5,7 +5,7 @@ * Copyright(c) 2011 MikuToga Partners */ -package jp.sourceforge.mikutoga.vmd.parser; +package jp.sfjp.mikutoga.vmd.parser; import jp.sfjp.mikutoga.bin.parser.LoopHandler; import jp.sfjp.mikutoga.bin.parser.MmdFormatException; diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdCameraParser.java b/src/main/java/jp/sfjp/mikutoga/vmd/parser/VmdCameraParser.java similarity index 94% rename from src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdCameraParser.java rename to src/main/java/jp/sfjp/mikutoga/vmd/parser/VmdCameraParser.java index 119d599..57e7e64 100644 --- a/src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdCameraParser.java +++ b/src/main/java/jp/sfjp/mikutoga/vmd/parser/VmdCameraParser.java @@ -5,17 +5,17 @@ * Copyright(c) 2011 MikuToga Partners */ -package jp.sourceforge.mikutoga.vmd.parser; +package jp.sfjp.mikutoga.vmd.parser; import java.io.IOException; -import java.io.InputStream; -import jp.sfjp.mikutoga.bin.parser.CommonParser; +import jp.sfjp.mikutoga.bin.parser.BinParser; import jp.sfjp.mikutoga.bin.parser.MmdFormatException; +import jp.sfjp.mikutoga.bin.parser.ProxyParser; /** * VMDモーションファイルのカメラモーションパーサ。 */ -class VmdCameraParser extends CommonParser{ +class VmdCameraParser extends ProxyParser{ private static final int BZ_SIZE = 4; // 4byte Bezier parameter private static final int BZXYZ_SIZE = BZ_SIZE * 3; // XYZ Bezier @@ -30,10 +30,10 @@ class VmdCameraParser extends CommonParser{ /** * コンストラクタ。 - * @param source 入力ソース + * @param parser 委譲先パーサ */ - VmdCameraParser(InputStream source){ - super(source); + VmdCameraParser(BinParser parser){ + super(parser); return; } diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdLightingHandler.java b/src/main/java/jp/sfjp/mikutoga/vmd/parser/VmdLightingHandler.java similarity index 99% rename from src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdLightingHandler.java rename to src/main/java/jp/sfjp/mikutoga/vmd/parser/VmdLightingHandler.java index ee39f92..065be3a 100644 --- a/src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdLightingHandler.java +++ b/src/main/java/jp/sfjp/mikutoga/vmd/parser/VmdLightingHandler.java @@ -5,7 +5,7 @@ * Copyright(c) 2011 MikuToga Partners */ -package jp.sourceforge.mikutoga.vmd.parser; +package jp.sfjp.mikutoga.vmd.parser; import jp.sfjp.mikutoga.bin.parser.LoopHandler; import jp.sfjp.mikutoga.bin.parser.MmdFormatException; diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdLightingParser.java b/src/main/java/jp/sfjp/mikutoga/vmd/parser/VmdLightingParser.java similarity index 92% rename from src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdLightingParser.java rename to src/main/java/jp/sfjp/mikutoga/vmd/parser/VmdLightingParser.java index 2ef64aa..49a51d9 100644 --- a/src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdLightingParser.java +++ b/src/main/java/jp/sfjp/mikutoga/vmd/parser/VmdLightingParser.java @@ -5,29 +5,29 @@ * Copyright(c) 2011 MikuToga Partners */ -package jp.sourceforge.mikutoga.vmd.parser; +package jp.sfjp.mikutoga.vmd.parser; import java.io.IOException; -import java.io.InputStream; -import jp.sfjp.mikutoga.bin.parser.CommonParser; +import jp.sfjp.mikutoga.bin.parser.BinParser; import jp.sfjp.mikutoga.bin.parser.MmdFormatException; +import jp.sfjp.mikutoga.bin.parser.ProxyParser; /** * VMDモーションファイルのライティング情報パーサ。 * 照明光源演出データと影演出データを含む。 *

古い版のVMDファイルには影演出データが記述されていない場合がある。 */ -class VmdLightingParser extends CommonParser { +class VmdLightingParser extends ProxyParser { private VmdLightingHandler handler = VmdUnifiedHandler.EMPTY; /** * コンストラクタ。 - * @param source 入力ソース + * @param parser 委譲先パーサ */ - VmdLightingParser(InputStream source){ - super(source); + VmdLightingParser(BinParser parser){ + super(parser); return; } diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdParser.java b/src/main/java/jp/sfjp/mikutoga/vmd/parser/VmdParser.java similarity index 67% rename from src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdParser.java rename to src/main/java/jp/sfjp/mikutoga/vmd/parser/VmdParser.java index 264e787..44e730e 100644 --- a/src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdParser.java +++ b/src/main/java/jp/sfjp/mikutoga/vmd/parser/VmdParser.java @@ -5,10 +5,12 @@ * Copyright(c) 2011 MikuToga Partners */ -package jp.sourceforge.mikutoga.vmd.parser; +package jp.sfjp.mikutoga.vmd.parser; import java.io.IOException; import java.io.InputStream; +import jp.sfjp.mikutoga.bin.parser.BinParser; +import jp.sfjp.mikutoga.bin.parser.CommonParser; import jp.sfjp.mikutoga.bin.parser.MmdFormatException; /** @@ -23,7 +25,9 @@ public class VmdParser { private final VmdLightingParser lightingParser; private VmdBasicHandler basicHandler = VmdUnifiedHandler.EMPTY; - private boolean strictMode = true; + + private boolean ignoreName = true; + private boolean redundantCheck = false; /** @@ -37,9 +41,11 @@ public class VmdParser { if(source == null) throw new NullPointerException(); this.source = source; - this.basicParser = new VmdBasicParser(source); - this.cameraParser = new VmdCameraParser(source); - this.lightingParser = new VmdLightingParser(source); + BinParser parser = new CommonParser(this.source); + + this.basicParser = new VmdBasicParser(parser); + this.cameraParser = new VmdCameraParser(parser); + this.lightingParser = new VmdLightingParser(parser); return; } @@ -88,18 +94,28 @@ public class VmdParser { } /** - * 厳密なパース(Strict-mode)を行うか否か設定する。 - * デフォルトではStrict-modeはオン。 - *

Strict-mode下では、 - * ボーンモーションの冗長な補間情報の一貫性チェックが行われ、 - * モデル名がなんであろうとカメラ・ライティングデータのパースを試みる。 + * カメラ・ライティングデータのパースを試みるか否かの判断で、 + * 特殊モデル名判定を無視するか否か設定する。 + * デフォルトではモデル名を無視。 *

※MMDVer7.30前後のVMD出力不具合を回避したい場合は、 - * Strict-modeをオフにするとパースに成功する場合がある。 - * @param mode Strict-modeに設定したければtrue + * オフにするとパースに成功する場合がある。 + * @param mode モデル名を無視するならtrue */ - public void setStrictMode(boolean mode){ - this.strictMode = mode; - this.basicParser.setStrictMode(this.strictMode); + public void setIgnoreName(boolean mode){ + this.ignoreName = mode; + return; + } + + /** + * ボーンモーション補間情報冗長部のチェックを行うか否か設定する。 + * デフォルトではチェックを行わない。 + *

※MMDVer7.30前後のVMD出力不具合を回避したい場合は、 + * オフにするとパースに成功する場合がある。 + * @param mode チェックさせたければtrue + */ + public void setRedundantCheck(boolean mode){ + this.redundantCheck = mode; + this.basicParser.setRedundantCheck(mode); return; } @@ -109,6 +125,9 @@ public class VmdParser { * @throws MmdFormatException フォーマットエラー */ public void parseVmd() throws IOException, MmdFormatException { + setIgnoreName(this.ignoreName); + setRedundantCheck(this.redundantCheck); + this.basicHandler.vmdParseStart(); parseBody(); @@ -130,7 +149,7 @@ public class VmdParser { private void parseBody() throws IOException, MmdFormatException{ this.basicParser.parse(); - if(this.basicParser.hasStageActName() || this.strictMode){ + if(this.basicParser.hasStageActName() || this.ignoreName){ this.cameraParser.parse(); this.lightingParser.parse(); } diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdUnifiedHandler.java b/src/main/java/jp/sfjp/mikutoga/vmd/parser/VmdUnifiedHandler.java similarity index 92% rename from src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdUnifiedHandler.java rename to src/main/java/jp/sfjp/mikutoga/vmd/parser/VmdUnifiedHandler.java index ddc6197..cca1a51 100644 --- a/src/main/java/jp/sourceforge/mikutoga/vmd/parser/VmdUnifiedHandler.java +++ b/src/main/java/jp/sfjp/mikutoga/vmd/parser/VmdUnifiedHandler.java @@ -5,7 +5,7 @@ * Copyright(c) 2013 MikuToga Partners */ -package jp.sourceforge.mikutoga.vmd.parser; +package jp.sfjp.mikutoga.vmd.parser; import jp.sfjp.mikutoga.corelib.EmptyProxyFactory; diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/parser/package-info.java b/src/main/java/jp/sfjp/mikutoga/vmd/parser/package-info.java similarity index 91% rename from src/main/java/jp/sourceforge/mikutoga/vmd/parser/package-info.java rename to src/main/java/jp/sfjp/mikutoga/vmd/parser/package-info.java index 842b618..79a9c01 100644 --- a/src/main/java/jp/sourceforge/mikutoga/vmd/parser/package-info.java +++ b/src/main/java/jp/sfjp/mikutoga/vmd/parser/package-info.java @@ -12,6 +12,6 @@ * 各種データの出現に応じてコールバックメソッドを次々と呼び出してくれる。 */ -package jp.sourceforge.mikutoga.vmd.parser; +package jp.sfjp.mikutoga.vmd.parser; /* EOF */ diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/VmdConst.java b/src/main/java/jp/sourceforge/mikutoga/vmd/VmdConst.java deleted file mode 100644 index a114f0c..0000000 --- a/src/main/java/jp/sourceforge/mikutoga/vmd/VmdConst.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * VMD constant data - * - * License : The MIT License - * Copyright(c) 2011 MikuToga Partners - */ - -package jp.sourceforge.mikutoga.vmd; - -import java.nio.charset.Charset; - -/** - * VMDファイルフォーマットの各種定数。 - */ -public final class VmdConst { - - /** - * VMDファイルヘッダ長。バイト単位。 - */ - public static final int HEADER_LENGTH = 30; - - /** - * ファイルヘッダ部先頭のASCIIコード相当部。 - */ - public static final String MAGIC_TXT = "Vocaloid Motion Data 0002"; - - /** - * モデル名最大長。バイト単位。 - */ - public static final int MODELNAME_MAX = 20; - - /** - * ボーン名最大長。バイト単位。 - */ - public static final int BONENAME_MAX = 15; - - /** - * モーフ名最大長。バイト単位。 - */ - public static final int MORPHNAME_MAX = 15; - - /** - * カメラもしくはライティングデータに便宜上割り当てられるモデル名。 - */ - public static final String MODELNAME_STAGEACT = - "\u30ab\u30e1\u30e9\u30fb\u7167\u660e"; // "カメラ・照明"; - - /** - * 便宜上割り振られる特殊なモーフ名。 - */ - public static final String MORPHNAME_BASE = "base"; - - - private static final Charset CS_ASCII = Charset.forName("US-ASCII"); - private static final byte[] MAGIC_BYTES; - - - static{ - MAGIC_BYTES = createMagicHeader(); - assert MAGIC_BYTES.length <= HEADER_LENGTH; - - assert "カメラ・照明".equals(MODELNAME_STAGEACT); - } - - - /** - * 隠しコンストラクタ。 - */ - private VmdConst(){ - super(); - assert false; - throw new AssertionError(); - } - - /** - * ヘッダ先頭のマジックバイト列を生成する。 - * @return マジックバイト列 - */ - public static byte[] createMagicHeader(){ - byte[] result = (MAGIC_TXT + '\0').getBytes(CS_ASCII); - return result; - } - - /** - * バイト列先頭がマジックヘッダで始まるか検査する。 - * @param array 検査対象のバイト列。 - * @return マジックヘッダが検出されればtrue。 - */ - public static boolean startsWithMagic(byte[] array){ - if(MAGIC_BYTES.length > array.length) return false; - - for(int idx = 0; idx < MAGIC_BYTES.length; idx++){ - if(array[idx] != MAGIC_BYTES[idx]) return false; - } - - return true; - } - - /** - * カメラやライティングなどのステージ演出データの可能性があるか、 - * モデル名から推測する。 - * モデル名が"カメラ・照明"である場合、 - * そのモーションファイルはほぼ - * カメラ・ライティング用ステージ演出データであると推測される。 - * @param modelName モデル名 - * @return モデル名にカメラもしくはライティングの可能性があるならtrue - */ - public static boolean isStageActName(String modelName){ - if(MODELNAME_STAGEACT.equals(modelName)) return true; - return false; - } - - /** - * 特殊なモーフ名「base」か否か判定する。 - * @param morphName モーフ名 - * @return モーフ名が「base」ならtrue - */ - public static boolean isBaseMorphName(String morphName){ - if(MORPHNAME_BASE.equals(morphName)) return true; - return false; - } - -} diff --git a/src/main/java/jp/sourceforge/mikutoga/xml/AbstractXmlExporter.java b/src/main/java/jp/sourceforge/mikutoga/xml/AbstractXmlExporter.java new file mode 100644 index 0000000..8344b4c --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/xml/AbstractXmlExporter.java @@ -0,0 +1,664 @@ +/* + * abstract xml exporter + * + * License : The MIT License + * Copyright(c) 2013 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.xml; + +import java.io.IOException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.xml.bind.DatatypeConverter; + +/** + * XMLエクスポータの半実装。 + * UCS4は未サポート。 + */ +abstract class AbstractXmlExporter implements XmlExporter{ + + /** デフォルトの改行文字列。 */ + private static final String DEF_NL = "\n"; // 0x0a(LF) + /** デフォルトのインデント単位。 */ + private static final String DEF_INDENT_UNIT = "\u0020\u0020"; // ␣␣ + + private static final char CH_SP = '\u0020'; // ␣ + private static final char CH_YEN = '\u00a5'; // ¥ + private static final char CH_BSLASH = (char)0x005c; // \ + private static final char CH_DQ = '\u0022'; // " + private static final char CH_SQ = (char)0x0027; // ' + private static final char CH_EQ = '='; // = + private static final char CH_LT = '<'; + private static final char CH_GT = '>'; + + private static final String COMM_START = ""; + + private static final Pattern NUM_FUZZY = + Pattern.compile("([^.]*\\.[0-9][0-9]*?)0+"); + + private static final String REF_HEX = "&#x"; + private static final int HEX_EXP = 4; // 2 ** 4 == 16 + private static final int MASK_1HEX = (1 << HEX_EXP) - 1; // 0b00001111 + private static final int MAX_OCTET = (1 << Byte.SIZE) - 1; // 0xff + private static final char[] HEXCHAR_TABLE = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F', + }; + + static{ + assert HEX_EXP * 2 == Byte.SIZE; + assert HEXCHAR_TABLE.length == (1 << HEX_EXP); + } + + + private boolean basicLatinOnlyOut = true; + private String newline = DEF_NL; + private String indentUnit = DEF_INDENT_UNIT; + private int indentNest = 0; + + + /** + * コンストラクタ。 + */ + protected AbstractXmlExporter(){ + super(); + return; + } + + + /** + * ASCIIコード相当(UCS:Basic-Latin)の文字か否か判定する。 + *

※ Basic-Latinには各種制御文字も含まれる。 + * @param ch 判定対象文字 + * @return Basic-Latin文字ならtrue + * + * Unicode 6.2 Controls and Basic Latin + * + */ + protected static boolean isBasicLatin(char ch){ + if('\u0000' <= ch && ch <= '\u007f'){ + return true; + } + return false; + } + + /** + * 冗長な実数出力を抑止する。 + *

DatatypeConverterにおけるJDK1.6系と1.7系の仕様変更を吸収する。 + *

0.001fは"0.0010"ではなく"0.001"と出力される。 + *

指数表記での冗長桁は無視する。 + * @param numTxt 実数表記 + * @return 冗長桁が抑止された実数表記 + * @see javax.xml.bind.DatatypeConverter + */ + protected static String chopFuzzyZero(String numTxt){ + String result; + + Matcher matcher = NUM_FUZZY.matcher(numTxt); + if(matcher.matches()){ + result = matcher.group(1); + }else{ + result = numTxt; + } + + return result; + } + + + /** + * {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public abstract void flush() throws IOException; + + /** + * {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public abstract void close() throws IOException; + + /** + * {@inheritDoc} + * @param ch {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public abstract XmlExporter putRawCh(char ch) + throws IOException; + + /** + * {@inheritDoc} + * @param seq {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public abstract XmlExporter putRawText(CharSequence seq) + throws IOException; + + + /** + * {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public XmlExporter sp() throws IOException{ + putRawCh(CH_SP); + return this; + } + + /** + * {@inheritDoc} + * @param count {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public XmlExporter sp(int count) throws IOException{ + for(int ct = 1; ct <= count; ct++){ + sp(); + } + return this; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public String getNewLine(){ + return this.newline; + } + + /** + * {@inheritDoc} + * @param newLine {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + */ + @Override + public void setNewLine(String newLine) throws NullPointerException{ + if(newLine == null) throw new NullPointerException(); + this.newline = newLine; + return; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public XmlExporter ln() throws IOException{ + putRawText(getNewLine()); + return this; + } + + /** + * {@inheritDoc} + * @param count {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public XmlExporter ln(int count) throws IOException{ + for(int ct = 1; ct <= count; ct++){ + ln(); + } + return this; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public String getIndentUnit(){ + return this.indentUnit; + } + + /** + * {@inheritDoc} + * @param indUnit {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + */ + @Override + public void setIndentUnit(String indUnit) throws NullPointerException{ + if(indUnit == null) throw new NullPointerException(); + this.indentUnit = indUnit; + return; + } + + /** + * {@inheritDoc} + */ + @Override + public void pushNest(){ + this.indentNest++; + return; + } + + /** + * {@inheritDoc} + */ + @Override + public void popNest(){ + this.indentNest--; + if(this.indentNest < 0) this.indentNest = 0; + return; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public int getIndentLevel(){ + return this.indentNest; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public XmlExporter ind() throws IOException{ + int level = getIndentLevel(); + for(int ct = 1; ct <= level; ct++){ + putRawText(getIndentUnit()); + } + return this; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public boolean isBasicLatinOnlyOut(){ + return this.basicLatinOnlyOut; + } + + /** + * {@inheritDoc} + * @param bool {@inheritDoc} + */ + @Override + public void setBasicLatinOnlyOut(boolean bool){ + this.basicLatinOnlyOut = bool; + return; + } + + /** + * {@inheritDoc} + * @param ch {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public XmlExporter putCharRef2Hex(char ch) throws IOException{ + if(ch > MAX_OCTET) return putCharRef4Hex(ch); + + int ibits = ch; // 常に正なので符号拡張なし + + int idx4 = ibits & MASK_1HEX; + ibits >>= HEX_EXP; + int idx3 = ibits & MASK_1HEX; + + char hex3 = HEXCHAR_TABLE[idx3]; + char hex4 = HEXCHAR_TABLE[idx4]; + + putRawText(REF_HEX).putRawCh(hex3).putRawCh(hex4) + .putRawCh(';'); + + return this; + } + + /** + * {@inheritDoc} + * @param ch {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public XmlExporter putCharRef4Hex(char ch) throws IOException{ + int ibits = ch; // 常に正なので符号拡張なし + + int idx4 = ibits & MASK_1HEX; + ibits >>= HEX_EXP; + int idx3 = ibits & MASK_1HEX; + ibits >>= HEX_EXP; + int idx2 = ibits & MASK_1HEX; + ibits >>= HEX_EXP; + int idx1 = ibits & MASK_1HEX; + + char hex1 = HEXCHAR_TABLE[idx1]; + char hex2 = HEXCHAR_TABLE[idx2]; + char hex3 = HEXCHAR_TABLE[idx3]; + char hex4 = HEXCHAR_TABLE[idx4]; + + putRawText(REF_HEX).putRawCh(hex1).putRawCh(hex2) + .putRawCh(hex3).putRawCh(hex4) + .putRawCh(';'); + + return this; + } + + /** + * {@inheritDoc} + * @param ch {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public XmlExporter putCh(char ch) throws IOException{ + if(Character.isISOControl(ch)){ + putCharRef2Hex(ch); + return this; + } + + String escTxt; + switch(ch){ + case '&': escTxt = "&"; break; + case CH_LT: escTxt = "<"; break; + case CH_GT: escTxt = ">"; break; + case CH_DQ: escTxt = """; break; + case CH_SQ: escTxt = "'"; break; + default: escTxt = null; break; + } + + if(escTxt != null){ + putRawText(escTxt); + }else{ + putRawCh(ch); + } + + return this; + } + + /** + * {@inheritDoc} + * @param content {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public XmlExporter putContent(CharSequence content) + throws IOException{ + int length = content.length(); + + char prev = '\0'; + for(int pos = 0; pos < length; pos++){ + char ch = content.charAt(pos); + + if( isBasicLatinOnlyOut() && ! isBasicLatin(ch) ){ + putCharRef4Hex(ch); + }else if(ch == CH_YEN){ + putRawCh(CH_BSLASH); + }else if(Character.isSpaceChar(ch)){ + if(ch == CH_SP && prev != CH_SP){ + putRawCh(ch); + }else{ + putCharRef2Hex(ch); + } + }else{ + putCh(ch); + } + + prev = ch; + } + + return this; + } + + /** + * {@inheritDoc} + * @param comment {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public XmlExporter putCommentContent(CharSequence comment) + throws IOException{ + int length = comment.length(); + + char prev = '\0'; + for(int pos = 0; pos < length; pos++){ + char ch = comment.charAt(pos); + + if(ch == '\n'){ + ln(); + }else if('\u0000' <= ch && ch <= '\u001f'){ + putRawCh((char)('\u2400' + ch)); + }else if(ch == '\u007f'){ + putRawCh('\u2421'); + }else if(prev == '-' && ch == '-'){ + sp().putRawCh(ch); + }else{ + putRawCh(ch); + } + + prev = ch; + } + + return this; + } + + /** + * {@inheritDoc} + * @param comment {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public XmlExporter putLineComment(CharSequence comment) + throws IOException{ + putRawText(COMM_START).sp(); + putCommentContent(comment); + sp().putRawText(COMM_END); + return this; + } + + /** + * {@inheritDoc} + * @param comment {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public XmlExporter putBlockComment(CharSequence comment) + throws IOException{ + putRawText(COMM_START).ln(); + + putCommentContent(comment); + + int commentLength = comment.length(); + if(commentLength > 0){ + char lastCh = comment.charAt(commentLength - 1); + if(lastCh != '\n'){ + ln(); + } + } + + putRawText(COMM_END).ln(); + + return this; + } + + /** + * {@inheritDoc} + * @param tagName {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public XmlExporter putOpenSTag(CharSequence tagName) + throws IOException{ + putRawCh(CH_LT); + putRawText(tagName); + return this; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public XmlExporter putCloseSTag() + throws IOException{ + putRawCh(CH_GT); + return this; + } + + /** + * {@inheritDoc} + * @param tagName {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public XmlExporter putSimpleSTag(CharSequence tagName) + throws IOException{ + putRawCh(CH_LT); + putRawText(tagName); + putRawCh(CH_GT); + return this; + } + + /** + * {@inheritDoc} + * @param tagName {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public XmlExporter putETag(CharSequence tagName) + throws IOException{ + putRawText(""); + return this; + } + + /** + * {@inheritDoc} + * @param iVal {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public XmlExporter putXsdInt(int iVal) throws IOException{ + String value = DatatypeConverter.printInt(iVal); + putRawText(value); + return this; + } + + /** + * {@inheritDoc} + * @param fVal {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public XmlExporter putXsdFloat(float fVal) throws IOException{ + String value = DatatypeConverter.printFloat(fVal); + value = chopFuzzyZero(value); + putRawText(value); + return this; + } + + /** + * {@inheritDoc} + * @param attrName {@inheritDoc} + * @param iVal {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public XmlExporter putIntAttr(CharSequence attrName, + int iVal) + throws IOException{ + putRawText(attrName).putRawCh(CH_EQ); + + putRawCh(CH_DQ); + putXsdInt(iVal); + putRawCh(CH_DQ); + + return this; + } + + /** + * {@inheritDoc} + * @param attrName {@inheritDoc} + * @param fVal {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public XmlExporter putFloatAttr(CharSequence attrName, + float fVal) + throws IOException{ + putRawText(attrName).putRawCh(CH_EQ); + + putRawCh(CH_DQ); + putXsdFloat(fVal); + putRawCh(CH_DQ); + + return this; + } + + /** + * {@inheritDoc} + * @param attrName {@inheritDoc} + * @param content {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public XmlExporter putAttr(CharSequence attrName, + CharSequence content) + throws IOException{ + putRawText(attrName).putRawCh(CH_EQ); + + putRawCh(CH_DQ); + putContent(content); + putRawCh(CH_DQ); + + return this; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/xml/BasicXmlExporter.java b/src/main/java/jp/sourceforge/mikutoga/xml/BasicXmlExporter.java index b429c55..5e3c1fa 100644 --- a/src/main/java/jp/sourceforge/mikutoga/xml/BasicXmlExporter.java +++ b/src/main/java/jp/sourceforge/mikutoga/xml/BasicXmlExporter.java @@ -14,60 +14,18 @@ import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.nio.charset.Charset; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import javax.xml.bind.DatatypeConverter; /** - * 各種XMLエクスポータの基本機能。 - * UCS4は未サポート。 + * Appendable用XMLエクスポータ実装。 */ -public class BasicXmlExporter { +public class BasicXmlExporter extends AbstractXmlExporter{ /** デフォルトエンコーディング。 */ private static final Charset CS_UTF8 = Charset.forName("UTF-8"); - /** デフォルトの改行文字列。 */ - private static final String DEF_NL = "\n"; // 0x0a(LF) - /** デフォルトのインデント単位。 */ - private static final String DEF_INDENT_UNIT = "\u0020\u0020"; // ␣␣ - - private static final char CH_SP = '\u0020'; // ␣ - private static final char CH_YEN = '\u00a5'; // ¥ - private static final char CH_BSLASH = (char)0x005c; // \ - private static final char CH_DQ = '\u0022'; // " - private static final char CH_SQ = (char)0x0027; // ' - - private static final String COMM_START = ""; - private static final String REF_HEX = "&#x"; - - private static final Pattern NUM_FUZZY = - Pattern.compile("([^.]*\\.[0-9][0-9]*?)0+"); - - private static final int HEX_EXP = 4; // 2 ** 4 == 16 - private static final int MASK_1HEX = (1 << HEX_EXP) - 1; // 0b00001111 - private static final int MAX_OCTET = (1 << Byte.SIZE) - 1; // 0xff - private static final char[] HEXCHAR_TABLE = { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'A', 'B', 'C', 'D', 'E', 'F', - }; - - - static{ - assert HEX_EXP * 2 == Byte.SIZE; - assert HEXCHAR_TABLE.length == (1 << HEX_EXP); - } - private final Appendable appendable; - private String newline = DEF_NL; - private String indentUnit = DEF_INDENT_UNIT; - - private int indentNest = 0; - private boolean basicLatinOnlyOut = true; - /** * コンストラクタ。 @@ -105,104 +63,35 @@ public class BasicXmlExporter { /** - * ASCIIコード相当(UCS:Basic-Latin)の文字か否か判定する。 - *

※ Basic-Latinには各種制御文字も含まれる。 - * @param ch 判定対象文字 - * @return Basic-Latin文字ならtrue - */ - public static boolean isBasicLatin(char ch){ - if('\u0000' <= ch && ch <= '\u007f'){ - return true; - } - return false; - } - - /** - * 冗長な実数出力を抑止する。 - *

DatatypeConverterにおけるJDK1.6系と1.7系の仕様変更を吸収する。 - *

0.001fは"0.0010"ではなく"0.001"と出力される。 - *

指数表記での冗長桁は無視する。 - * @param numTxt 実数表記 - * @return 冗長桁が抑止された実数表記 - */ - public static String chopFuzzyZero(String numTxt){ - String result; - - Matcher matcher = NUM_FUZZY.matcher(numTxt); - if(matcher.matches()){ - result = matcher.group(1); - }else{ - result = numTxt; - } - - return result; - } - - - /** - * BasicLatin文字だけで出力するか設定する。 - *

BasicLatin以外の文字(≒日本語)を、そのまま出力するか、 - * 文字参照で出力するか、の設定が可能。 - *

コメント部中身は対象外。 - * @param bool BasicLatin文字だけで出力するならtrue - */ - public void setBasicLatinOnlyOut(boolean bool){ - this.basicLatinOnlyOut = bool; - return; - } - - /** - * BasicLatin文字だけを出力する状態か判定する。 - *

コメント部中身は対象外。 - * @return BasicLatin文字だけで出力するならtrue - */ - public boolean isBasicLatinOnlyOut(){ - return this.basicLatinOnlyOut; - } - - /** - * 改行文字列を設定する。 - * @param newLine 改行文字列 - * @throws NullPointerException 引数がnull - */ - public void setNewLine(String newLine) throws NullPointerException{ - if(newLine == null) throw new NullPointerException(); - this.newline = newLine; - return; - } - - /** - * 改行文字列を返す。 - * @return 改行文字列 - */ - public String getNewLine(){ - return this.newline; - } - - /** - * インデント単位文字列を設定する。 - *

デフォルトでは空白2個。 - * @param indUnit インデント単位文字列。 - * @throws NullPointerException 引数がnull + * {@inheritDoc} + * @param ch {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} */ - public void setIndentUnit(String indUnit) throws NullPointerException{ - if(indUnit == null) throw new NullPointerException(); - this.indentUnit = indUnit; - return; + @Override + public BasicXmlExporter putRawCh(char ch) throws IOException{ + this.appendable.append(ch); + return this; } /** - * インデント単位文字列を返す。 - * @return インデント単位文字列 + * {@inheritDoc} + * @param seq {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} */ - public String getIndentUnit(){ - return this.indentUnit; + @Override + public BasicXmlExporter putRawText(CharSequence seq) throws IOException{ + this.appendable.append(seq); + return this; } /** + * {@inheritDoc} * 可能であれば出力をフラッシュする。 - * @throws IOException 出力エラー + * @throws IOException {@inheritDoc} */ + @Override public void flush() throws IOException{ if(this.appendable instanceof Flushable){ ((Flushable)this.appendable).flush(); @@ -211,9 +100,11 @@ public class BasicXmlExporter { } /** + * {@inheritDoc} * 可能であれば出力をクローズする。 - * @throws IOException 出力エラー + * @throws IOException {@inheritDoc} */ + @Override public void close() throws IOException{ if(this.appendable instanceof Closeable){ ((Closeable)this.appendable).close(); @@ -221,387 +112,4 @@ public class BasicXmlExporter { return; } - /** - * 1文字出力する。 - * @param ch 文字 - * @return this本体 - * @throws IOException 出力エラー - */ - public BasicXmlExporter putRawCh(char ch) throws IOException{ - this.appendable.append(ch); - return this; - } - - /** - * 文字列を出力する。 - * @param seq 文字列 - * @return this本体 - * @throws IOException 出力エラー - */ - public BasicXmlExporter putRawText(CharSequence seq) throws IOException{ - this.appendable.append(seq); - return this; - } - - /** - * 指定された文字を16進2桁の文字参照形式で出力する。 - * 2桁で出力できない場合(>0x00ff)は4桁で出力する。 - * @param ch 文字 - * @return this本体 - * @throws IOException 出力エラー - */ - public BasicXmlExporter putCharRef2Hex(char ch) throws IOException{ - if(ch > MAX_OCTET) return putCharRef4Hex(ch); - - int ibits = ch; // 常に正なので符号拡張なし - - int idx4 = ibits & MASK_1HEX; - ibits >>= HEX_EXP; - int idx3 = ibits & MASK_1HEX; - - char hex3 = HEXCHAR_TABLE[idx3]; - char hex4 = HEXCHAR_TABLE[idx4]; - - putRawText(REF_HEX).putRawCh(hex3).putRawCh(hex4) - .putRawCh(';'); - - return this; - } - - /** - * 指定された文字を16進4桁の文字参照形式で出力する。 - * UCS4に伴うサロゲートペアは未サポート - * @param ch 文字 - * @return this本体 - * @throws IOException 出力エラー - */ - public BasicXmlExporter putCharRef4Hex(char ch) throws IOException{ - int ibits = ch; // 常に正なので符号拡張なし - - int idx4 = ibits & MASK_1HEX; - ibits >>= HEX_EXP; - int idx3 = ibits & MASK_1HEX; - ibits >>= HEX_EXP; - int idx2 = ibits & MASK_1HEX; - ibits >>= HEX_EXP; - int idx1 = ibits & MASK_1HEX; - - char hex1 = HEXCHAR_TABLE[idx1]; - char hex2 = HEXCHAR_TABLE[idx2]; - char hex3 = HEXCHAR_TABLE[idx3]; - char hex4 = HEXCHAR_TABLE[idx4]; - - putRawText(REF_HEX).putRawCh(hex1).putRawCh(hex2) - .putRawCh(hex3).putRawCh(hex4) - .putRawCh(';'); - - return this; - } - - /** - * 要素の中身および属性値中身を出力する。 - *

XMLの構文規則を守る上で必要な各種エスケープ処理が行われる。 - * @param ch 文字 - * @return this本体 - * @throws IOException 出力エラー - */ - public BasicXmlExporter putCh(char ch) throws IOException{ - if(Character.isISOControl(ch)){ - putCharRef2Hex(ch); - return this; - } - - String escTxt; - switch(ch){ - case '&': escTxt = "&"; break; - case '<': escTxt = "<"; break; - case '>': escTxt = ">"; break; - case CH_DQ: escTxt = """; break; - case CH_SQ: escTxt = "'"; break; - default: escTxt = null; break; - } - - if(escTxt != null){ - putRawText(escTxt); - }else{ - putRawCh(ch); - } - - return this; - } - - /** - * 要素の中身および属性値中身を出力する。 - *

必要に応じてXML定義済み実体文字が割り振られた文字、 - * コントロールコード、および非BasicLatin文字がエスケープされる。 - *

半角通貨記号U+00A5はバックスラッシュU+005Cに置換される。 - *

連続するスペースU+0020の2文字目以降は文字参照化される。 - *

全角スペースその他空白文字は無条件に文字参照化される。 - * @param content 内容 - * @return this本体 - * @throws IOException 出力エラー - */ - public BasicXmlExporter putContent(CharSequence content) - throws IOException{ - int length = content.length(); - - char prev = '\0'; - for(int pos = 0; pos < length; pos++){ - char ch = content.charAt(pos); - - if( isBasicLatinOnlyOut() && ! isBasicLatin(ch) ){ - putCharRef4Hex(ch); - }else if(ch == CH_YEN){ - putRawCh(CH_BSLASH); - }else if(Character.isSpaceChar(ch)){ - if(ch == CH_SP && prev != CH_SP){ - putRawCh(ch); - }else{ - putCharRef2Hex(ch); - } - }else{ - putCh(ch); - } - - prev = ch; - } - - return this; - } - - /** - * 改行を出力する。 - * @return this本体 - * @throws IOException 出力エラー - */ - public BasicXmlExporter ln() throws IOException{ - this.appendable.append(this.newline); - return this; - } - - /** - * 改行を指定回数出力する。 - * @param count 改行回数。0以下の場合は何も出力しない。 - * @return this本体 - * @throws IOException 出力エラー - */ - public BasicXmlExporter ln(int count) throws IOException{ - for(int ct = 1; ct <= count; ct++){ - this.appendable.append(this.newline); - } - return this; - } - - /** - * 空白を出力する。 - * @return this本体 - * @throws IOException 出力エラー - */ - public BasicXmlExporter sp() throws IOException{ - this.appendable.append(CH_SP); - return this; - } - - /** - * 空白を指定回数出力する。 - * @param count 空白回数。0以下の場合は何も出力しない。 - * @return this本体 - * @throws IOException 出力エラー - */ - public BasicXmlExporter sp(int count) throws IOException{ - for(int ct = 1; ct <= count; ct++){ - this.appendable.append(CH_SP); - } - return this; - } - - /** - * インデントを出力する。 - * インデント単位文字列をネストレベル回数分出力する。 - * @return this本体 - * @throws IOException 出力エラー - */ - public BasicXmlExporter ind() throws IOException{ - for(int ct = 1; ct <= this.indentNest; ct++){ - putRawText(this.indentUnit); - } - return this; - } - - /** - * インデントレベルを一段下げる。 - */ - public void pushNest(){ - this.indentNest++; - return; - } - - /** - * インデントレベルを一段上げる。 - * インデントレベル0の状態をさらに上げようとした場合、何も起こらない。 - */ - public void popNest(){ - this.indentNest--; - if(this.indentNest < 0) this.indentNest = 0; - return; - } - - /** - * int値をXMLスキーマ準拠の形式で出力する。 - * @param iVal int値 - * @return this本体 - * @throws IOException 出力エラー - * @see java.lang.Integer#toString(int) - */ - public BasicXmlExporter putXsdInt(int iVal) throws IOException{ - String value = DatatypeConverter.printInt(iVal); - this.appendable.append(value); - return this; - } - - /** - * float値をXMLスキーマ準拠の形式で出力する。 - * @param fVal float値 - * @return this本体 - * @throws IOException 出力エラー - * @see java.lang.Float#toString(float) - */ - public BasicXmlExporter putXsdFloat(float fVal) throws IOException{ - String value = DatatypeConverter.printFloat(fVal); - this.appendable.append(value); - return this; - } - - /** - * 属性値を出力する。 - * @param attrName 属性名 - * @param content 属性内容 - * @return this本体 - * @throws IOException 出力エラー - */ - public BasicXmlExporter putAttr(CharSequence attrName, - CharSequence content) - throws IOException{ - putRawText(attrName).putRawCh('='); - - putRawCh('"'); - putContent(content); - putRawCh('"'); - - return this; - } - - /** - * int型属性値を出力する。 - * @param attrName 属性名 - * @param iVal int値 - * @return this本体 - * @throws IOException 出力エラー - */ - public BasicXmlExporter putIntAttr(CharSequence attrName, - int iVal) - throws IOException{ - String attrValue = DatatypeConverter.printInt(iVal); - putAttr(attrName, attrValue); - return this; - } - - /** - * float型属性値を出力する。 - * @param attrName 属性名 - * @param fVal float値 - * @return this本体 - * @throws IOException 出力エラー - */ - public BasicXmlExporter putFloatAttr(CharSequence attrName, - float fVal) - throws IOException{ - String attrValue = DatatypeConverter.printFloat(fVal); - attrValue = chopFuzzyZero(attrValue); - putAttr(attrName, attrValue); - return this; - } - - /** - * コメントの内容を出力する。 - *

コメント中の'\n'記号出現に伴い、 - * あらかじめ指定された改行文字が出力される。 - *

コメント中の'\n'以外のコントロールコードは - * Control Pictures(U+2400〜)で代替される。 - *

それ以外の非BasicLatin文字はそのまま出力される。 - *

連続するハイフン(-)記号間には強制的にスペースが挿入される。 - * @param comment コメント内容 - * @return this本体 - * @throws IOException 出力エラー - */ - public BasicXmlExporter putCommentContent(CharSequence comment) - throws IOException{ - int length = comment.length(); - - char prev = '\0'; - for(int pos = 0; pos < length; pos++){ - char ch = comment.charAt(pos); - - if(ch == '\n'){ - ln(); - }else if('\u0000' <= ch && ch <= '\u001f'){ - putRawCh((char)('\u2400' + ch)); - }else if(ch == '\u007f'){ - putRawCh('\u2421'); - }else if(prev == '-' && ch == '-'){ - sp().putRawCh(ch); - }else{ - putRawCh(ch); - } - - prev = ch; - } - - return this; - } - - /** - * 1行コメントを出力する。 - * コメント内部の頭及び末尾に空白が1つ挿入される。 - * @param comment コメント内容 - * @return this本体 - * @throws IOException 出力エラー - */ - public BasicXmlExporter putLineComment(CharSequence comment) - throws IOException{ - putRawText(COMM_START).sp(); - putCommentContent(comment); - sp().putRawText(COMM_END); - return this; - } - - /** - * ブロックコメントを出力する。 - *

コメント内部の頭の前に改行が出力される。 - *

コメント内部の末尾が改行でない場合、改行が挿入される。 - *

ブロックコメント末尾は改行で終わる。 - *

インデント設定は無視される。 - * @param comment コメント内容 - * @return this本体 - * @throws IOException 出力エラー - */ - public BasicXmlExporter putBlockComment(CharSequence comment) - throws IOException{ - putRawText(COMM_START).ln(); - - putCommentContent(comment); - - int commentLength = comment.length(); - if(commentLength > 0){ - char lastCh = comment.charAt(commentLength - 1); - if(lastCh != '\n'){ - ln(); - } - } - - putRawText(COMM_END).ln(); - - return this; - } - } diff --git a/src/main/java/jp/sourceforge/mikutoga/xml/LocalSchema.java b/src/main/java/jp/sourceforge/mikutoga/xml/LocalSchema.java deleted file mode 100644 index 92da58a..0000000 --- a/src/main/java/jp/sourceforge/mikutoga/xml/LocalSchema.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * local xml schema - * - * License : The MIT License - * Copyright(c) 2013 MikuToga Partners - */ - -package jp.sourceforge.mikutoga.xml; - -import java.io.BufferedInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URL; -import java.util.LinkedList; -import java.util.List; -import javax.xml.XMLConstants; -import javax.xml.transform.Source; -import javax.xml.transform.stream.StreamSource; -import javax.xml.validation.Schema; -import javax.xml.validation.SchemaFactory; -import org.xml.sax.SAXException; - -/** - * XML用各種スキーマのローカル参照解決基盤。 - */ -public abstract class LocalSchema { - - /** - * コンストラクタ。 - */ - protected LocalSchema(){ - super(); - return; - } - - - /** - * XML Schema 用のスキーマファクトリを返す。 - * @return スキーマファクトリ - */ - private static SchemaFactory newSchemaFactory(){ - SchemaFactory schemaFactory = - SchemaFactory.newInstance( - XMLConstants.W3C_XML_SCHEMA_NS_URI - ); - -// schemaFactory.setFeature(name, value); -// schemaFactory.setProperty(name, object); - - schemaFactory.setErrorHandler(BotherHandler.HANDLER); - - return schemaFactory; - } - - /** - * ローカルスキーマをロードする。 - *

任意のリゾルバを指定可能 - * @param resolver リゾルバ - * @param lscs ローカルスキーマ情報の配列 - * @return スキーマ - */ - public static Schema newSchema(XmlResourceResolver resolver, - LocalSchema... lscs ){ - List sourceList = new LinkedList(); - for(LocalSchema lsc : lscs){ - if(lsc == null) continue; - lsc.appendToUriMap(resolver); - - Source local = lsc.getLocalSchemaSource(); - if(local == null) continue; - sourceList.add(local); - } - - SchemaFactory schemaFactory = newSchemaFactory(); - schemaFactory.setResourceResolver(resolver); - - Source[] sources = new Source[sourceList.size()]; - sourceList.toArray(sources); - - Schema result; - try{ - if(sources.length <= 0){ - result = schemaFactory.newSchema(); - }else{ - result = schemaFactory.newSchema(sources); - } - }catch(SAXException e){ // Build error - assert false; - throw new AssertionError(e); - } - - return result; - } - - /** - * オリジナル版スキーマ定義のURIを返す。 - *

nullを返す場合は - * スキーマの自動判定&ダウンロードが求められていると見なされる。 - *

このクラスの実装では常にnullを返す。 - * @return オリジナル版スキーマのURL。 - */ - public abstract URI getOriginalSchema(); - - /** - * ローカルリソース版スキーマ定義のURIを返す。 - *

nullを返す場合は - * スキーマの自動判定&ダウンロードが求められていると見なされる。 - *

このクラスの実装では常にnullを返す。 - * @return ローカルリソース版スキーマのURL。 - */ - public abstract URI getLocalSchema(); - - /** - * スキーマのSourceを返す。 - *

ローカルスキーマのSourceを返す。 - * @return Source 見つからなければnull - */ - public Source getLocalSchemaSource(){ - URI uri; - - uri = getLocalSchema(); - if(uri == null) return null; - - URL url; - try{ - url = uri.toURL(); - }catch(MalformedURLException e){ // Build error - assert false; - throw new AssertionError(e); - } - - InputStream is; - try{ - is = url.openStream(); - }catch(IOException e){ // Build error - assert false; - throw new AssertionError(e); - } - is = new BufferedInputStream(is); - - Source result = new StreamSource(is); - - return result; - } - - /** - * ローカルで解決可能なリソース参照をリゾルバに追加登録する。 - * @param resolver リゾルバ - */ - public void appendToUriMap(XmlResourceResolver resolver){ - URI original = getOriginalSchema(); - URI local = getLocalSchema(); - - if(original == null) return; - - resolver.putRedirected(original, local); - - return; - } - -} diff --git a/src/main/java/jp/sourceforge/mikutoga/xml/LocalXmlResource.java b/src/main/java/jp/sourceforge/mikutoga/xml/LocalXmlResource.java new file mode 100644 index 0000000..5d009f2 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/xml/LocalXmlResource.java @@ -0,0 +1,31 @@ +/* + * xml local resource map + * + * License : The MIT License + * Copyright(c) 2013 olyutorskii + */ + +package jp.sourceforge.mikutoga.xml; + +import java.net.URI; + +/** + * 代用ローカルリソースの管理を行う。 + *

ネットワークを介したグローバルなリソースと、 + * アプリ上のローカルな代用リソースとを対応付ける。 + */ +public interface LocalXmlResource { + + /** + * オリジナル版XMLリソースのURIを返す。 + * @return オリジナル版リソースのURL。 + */ + URI getOriginalResource(); + + /** + * ローカル版XMLリソースのURIを返す。 + * @return ローカル版リソースのURL。 + */ + URI getLocalResource(); + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/xml/ProxyXmlExporter.java b/src/main/java/jp/sourceforge/mikutoga/xml/ProxyXmlExporter.java new file mode 100644 index 0000000..c1934ea --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/xml/ProxyXmlExporter.java @@ -0,0 +1,159 @@ +/* + * proxy xml exporter + * + * License : The MIT License + * Copyright(c) 2013 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.xml; + +import java.io.IOException; + +/** + * 委譲型XMLエクスポータ。 + */ +public class ProxyXmlExporter extends AbstractXmlExporter{ + + private final XmlExporter delegate; + + + /** + * コンストラクタ。 + * @param proxy 委譲先 + */ + public ProxyXmlExporter(XmlExporter proxy){ + super(); + this.delegate = proxy; + return; + } + + + /** + * {@inheritDoc} + * @param ch {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public XmlExporter putRawCh(char ch) throws IOException{ + return this.delegate.putRawCh(ch); + } + + /** + * {@inheritDoc} + * @param seq {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public XmlExporter putRawText(CharSequence seq) throws IOException{ + return this.delegate.putRawText(seq); + } + + /** + * {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public void flush() throws IOException{ + this.delegate.flush(); + return; + } + + /** + * {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public void close() throws IOException{ + this.delegate.close(); + return; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public boolean isBasicLatinOnlyOut(){ + return this.delegate.isBasicLatinOnlyOut(); + } + + /** + * {@inheritDoc} + * @param bool {@inheritDoc} + */ + @Override + public void setBasicLatinOnlyOut(boolean bool){ + this.delegate.setBasicLatinOnlyOut(bool); + return; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public String getNewLine(){ + return this.delegate.getNewLine(); + } + + /** + * {@inheritDoc} + * @param newLine {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + */ + @Override + public void setNewLine(String newLine) throws NullPointerException{ + this.delegate.setNewLine(newLine); + return; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public String getIndentUnit(){ + return this.delegate.getIndentUnit(); + } + + /** + * {@inheritDoc} + * @param indUnit {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + */ + @Override + public void setIndentUnit(String indUnit) throws NullPointerException{ + this.delegate.setIndentUnit(indUnit); + return; + } + + /** + * {@inheritDoc} + */ + @Override + public void pushNest(){ + this.delegate.pushNest(); + return; + } + + /** + * {@inheritDoc} + */ + @Override + public void popNest(){ + this.delegate.popNest(); + return; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public int getIndentLevel(){ + return this.delegate.getIndentLevel(); + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/xml/SchemaUtil.java b/src/main/java/jp/sourceforge/mikutoga/xml/SchemaUtil.java new file mode 100644 index 0000000..d8965aa --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/xml/SchemaUtil.java @@ -0,0 +1,151 @@ +/* + * xml schema utility + * + * License : The MIT License + * Copyright(c) 2013 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.xml; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import javax.xml.XMLConstants; +import javax.xml.transform.Source; +import javax.xml.transform.stream.StreamSource; +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; +import org.w3c.dom.ls.LSResourceResolver; +import org.xml.sax.SAXException; + +/** + * XMLスキーマの各種ビルダ。 + */ +public final class SchemaUtil { + + /** + * 隠しコンストラクタ。 + */ + private SchemaUtil(){ + assert false; + throw new AssertionError(); + } + + + /** + * XML Schema 用のスキーマファクトリを返す。 + * @return スキーマファクトリ + */ + public static SchemaFactory newSchemaFactory(){ + SchemaFactory result = newSchemaFactory(null); + return result; + } + + /** + * XML Schema 用のスキーマファクトリを返す。 + * @param resolver カスタムリゾルバ。nullも可。 + * @return スキーマファクトリ + */ + public static SchemaFactory newSchemaFactory( + LSResourceResolver resolver ){ + SchemaFactory schemaFactory = + SchemaFactory.newInstance( + XMLConstants.W3C_XML_SCHEMA_NS_URI + ); + +// schemaFactory.setFeature(name, value); +// schemaFactory.setProperty(name, object); + + schemaFactory.setErrorHandler(BotherHandler.HANDLER); + schemaFactory.setResourceResolver(resolver); + + return schemaFactory; + } + + /** + * ローカルリソースをSourceに変換する。 + * @param resource ローカルリソース + * @return XML Source + * @throws MalformedURLException 不正なURI + * @throws IOException オープンエラー + */ + private static Source toLocalSource(LocalXmlResource resource) + throws MalformedURLException, IOException{ + URI localUri = resource.getLocalResource(); + URL localUrl = localUri.toURL(); + + InputStream is = localUrl.openStream(); + is = new BufferedInputStream(is); + + Source result = new StreamSource(is); + return result; + } + + /** + * ローカルリソース群をSource群に変換する。 + * @param resArray ローカルリソースURI並び + * @return XML Source並び + * @throws MalformedURLException 不正なURI + * @throws IOException オープンエラー + */ + private static Source[] toLocalSourceArray(LocalXmlResource[] resArray) + throws MalformedURLException, IOException{ + List sourceList = new ArrayList(resArray.length); + + for(LocalXmlResource resource : resArray){ + Source localSource = toLocalSource(resource); + sourceList.add(localSource); + } + + Source[] result = new Source[sourceList.size()]; + result = sourceList.toArray(result); + return result; + } + + /** + * ローカルスキーマをロードする。 + *

任意のリゾルバを指定可能 + * @param resolver リゾルバ + * @param resArray ローカルスキーマ情報並び + * @return スキーマ + */ + public static Schema newSchema(XmlResourceResolver resolver, + LocalXmlResource... resArray ){ + for(LocalXmlResource resource : resArray){ + resolver.putRedirected(resource); + } + + Source[] sources; + try{ + sources = toLocalSourceArray(resArray); + }catch(IOException e){ // ビルド障害 + assert false; + throw new AssertionError(e); + } + + SchemaFactory schemaFactory = newSchemaFactory(resolver); + + Schema result; + try{ + if(sources.length <= 0){ + // ドキュメント埋め込みスキーマURLにリゾルバ経由でアクセス + result = schemaFactory.newSchema(); + }else{ + result = schemaFactory.newSchema(sources); + } + }catch(SAXException e){ // Build error + assert false; + throw new AssertionError(e); + } + + // TODO: Sourceを閉めるのは誰の責務? + + return result; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/xml/XmlExporter.java b/src/main/java/jp/sourceforge/mikutoga/xml/XmlExporter.java new file mode 100644 index 0000000..9055a5d --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/xml/XmlExporter.java @@ -0,0 +1,319 @@ +/* + * xml exporter + * + * License : The MIT License + * Copyright(c) 2013 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.xml; + +import java.io.Closeable; +import java.io.Flushable; +import java.io.IOException; + +/** + * XMLエクスポータ基本機能のセット。 + */ +public interface XmlExporter extends Flushable, Closeable{ + + /** + * 1文字出力する。 + * @param ch 文字 + * @return this本体 + * @throws IOException 出力エラー + */ + XmlExporter putRawCh(char ch) throws IOException; + + /** + * 文字列を出力する。 + * @param seq 文字列 + * @return this本体 + * @throws IOException 出力エラー + */ + XmlExporter putRawText(CharSequence seq) throws IOException; + + /** + * 空白を出力する。 + * @return this本体 + * @throws IOException 出力エラー + */ + XmlExporter sp() throws IOException; + + /** + * 空白を指定回数出力する。 + * @param count 空白回数。0以下の場合は何も出力しない。 + * @return this本体 + * @throws IOException 出力エラー + */ + XmlExporter sp(int count) throws IOException; + + /** + * 改行文字列を返す。 + * @return 改行文字列 + */ + String getNewLine(); + + /** + * 改行文字列を設定する。 + * @param newLine 改行文字列 + * @throws NullPointerException 引数がnull + */ + void setNewLine(String newLine) throws NullPointerException; + + /** + * 改行を出力する。 + * @return this本体 + * @throws IOException 出力エラー + */ + XmlExporter ln() throws IOException; + + /** + * 改行を指定回数出力する。 + * @param count 改行回数。0以下の場合は何も出力しない。 + * @return this本体 + * @throws IOException 出力エラー + */ + XmlExporter ln(int count) throws IOException; + + /** + * インデント単位文字列を返す。 + * @return インデント単位文字列 + */ + String getIndentUnit(); + + /** + * インデント単位文字列を設定する。 + *

デフォルトでは空白2個。 + * @param indUnit インデント単位文字列。 + * @throws NullPointerException 引数がnull + */ + void setIndentUnit(String indUnit) throws NullPointerException; + + /** + * インデントレベルを一段下げる。 + */ + void pushNest(); + + /** + * インデントレベルを一段上げる。 + * インデントレベル0の状態をさらに上げようとした場合、何も起こらない。 + */ + void popNest(); + + /** + * インデントレベルを返す。 + *

深さ1の場合1を返す。 + * @return インデントレベル + */ + int getIndentLevel(); + + /** + * インデントを出力する。 + * インデント単位文字列をネストレベル回数分出力する。 + * @return this本体 + * @throws IOException 出力エラー + */ + XmlExporter ind() throws IOException; + + /** + * BasicLatin文字だけを出力する状態か判定する。 + *

コメント部中身は対象外。 + * @return BasicLatin文字だけで出力するならtrue + */ + boolean isBasicLatinOnlyOut(); + + /** + * BasicLatin文字だけで出力するか設定する。 + *

BasicLatin以外の文字(≒日本語)を、そのまま出力するか、 + * 文字参照で出力するか、の設定が可能。 + *

コメント部中身は対象外。 + * @param bool BasicLatin文字だけで出力するならtrue + */ + void setBasicLatinOnlyOut(boolean bool); + + /** + * 指定された文字を16進2桁の文字参照形式で出力する。 + *

「A」は「&#x41;」になる。 + *

2桁で出力できない場合(>0x00ff)は4桁で出力する。 + * @param ch 文字 + * @return this本体 + * @throws IOException 出力エラー + * @see + * W3C XML1.1 Character Reference + * + */ + XmlExporter putCharRef2Hex(char ch) throws IOException; + + /** + * 指定された文字を16進4桁の文字参照形式で出力する。 + *

「亜」は「&#x4E9C;」になる。 + *

UCS4に伴うサロゲートペアは未サポート + * @param ch 文字 + * @return this本体 + * @throws IOException 出力エラー + * @see + * W3C XML1.1 Character Reference + * + */ + XmlExporter putCharRef4Hex(char ch) throws IOException; + + /** + * 要素の中身および属性値中身を出力する。 + *

XMLの構文規則を守る上で必要な各種エスケープ処理が行われる。 + * @param ch 文字 + * @return this本体 + * @throws IOException 出力エラー + */ + XmlExporter putCh(char ch) throws IOException; + + /** + * 要素の中身および属性値中身を出力する。 + *

必要に応じてXML定義済み実体文字が割り振られた文字、 + * コントロールコード、および非BasicLatin文字がエスケープされる。 + *

半角円通貨記号U+00A5はバックスラッシュU+005Cに置換される。 + *

連続するスペースU+0020の2文字目以降は文字参照化される。 + *

全角スペースその他空白文字は無条件に文字参照化される。 + * @param content 内容 + * @return this本体 + * @throws IOException 出力エラー + */ + XmlExporter putContent(CharSequence content) throws IOException; + + /** + * コメントの内容を出力する。 + *

コメント中の'\n'記号出現に伴い、 + * あらかじめ指定された改行文字が出力される。 + *

コメント中の'\n'以外のコントロールコードは + * Control Pictures(U+2400〜)で代替される。 + *

それ以外の非BasicLatin文字はそのまま出力される。 + *

連続するハイフン(-)記号間には強制的にスペースが挿入される。 + * @param comment コメント内容 + * @return this本体 + * @throws IOException 出力エラー + * + * Unicode 6.2 Controll Pictures + * + */ + XmlExporter putCommentContent(CharSequence comment) throws IOException; + + /** + * 1行コメントを出力する。 + * コメント内部の頭及び末尾に空白が1つ挿入される。 + * @param comment コメント内容 + * @return this本体 + * @throws IOException 出力エラー + */ + XmlExporter putLineComment(CharSequence comment) throws IOException; + + /** + * ブロックコメントを出力する。 + *

コメント内部の頭の前に改行が出力される。 + *

コメント内部の末尾が改行でない場合、改行が挿入される。 + *

ブロックコメント末尾は改行で終わる。 + *

インデント設定は無視される。 + * @param comment コメント内容 + * @return this本体 + * @throws IOException 出力エラー + */ + XmlExporter putBlockComment(CharSequence comment) throws IOException; + + /** + * 開始タグ開き表記を出力する。 + * @param tagName タグ名 + * @return this本体 + * @throws IOException 出力エラー + */ + XmlExporter putOpenSTag(CharSequence tagName) throws IOException; + + /** + * 開始タグ閉じ表記を出力する。 + * @return this本体 + * @throws IOException 出力エラー + */ + XmlExporter putCloseSTag() throws IOException; + + /** + * 属性の無いシンプルな開始タグ表記を出力する。 + * @param tagName タグ名 + * @return this本体 + * @throws IOException 出力エラー + */ + XmlExporter putSimpleSTag(CharSequence tagName) throws IOException; + + /** + * 終了タグ表記を出力する。 + * @param tagName タグ名 + * @return this本体 + * @throws IOException 出力エラー + */ + XmlExporter putETag(CharSequence tagName) throws IOException; + + /** + * 属性の無い単出タグ表記を出力する。 + * @param tagName タグ名 + * @return this本体 + * @throws IOException 出力エラー + */ + XmlExporter putSimpleEmpty(CharSequence tagName) throws IOException; + + /** + * 単出タグ閉じ表記を出力する。 + * @return this本体 + * @throws IOException 出力エラー + */ + XmlExporter putCloseEmpty() throws IOException; + + /** + * int値をXMLスキーマ準拠の形式で出力する。 + * @param iVal int値 + * @return this本体 + * @throws IOException 出力エラー + * @see java.lang.Integer#toString(int) + * @see + * XML Schema 1.1 Datatypes integer + * + */ + XmlExporter putXsdInt(int iVal) throws IOException; + + /** + * float値をXMLスキーマ準拠の形式で出力する。 + * @param fVal float値 + * @return this本体 + * @throws IOException 出力エラー + * @see + * XML Schema 1.1 Datatypes float Lexical Mapping + * + */ + XmlExporter putXsdFloat(float fVal) throws IOException; + + /** + * int型属性値を出力する。 + * @param attrName 属性名 + * @param iVal int値 + * @return this本体 + * @throws IOException 出力エラー + */ + XmlExporter putIntAttr(CharSequence attrName, int iVal) + throws IOException; + + /** + * float型属性値を出力する。 + * @param attrName 属性名 + * @param fVal float値 + * @return this本体 + * @throws IOException 出力エラー + */ + XmlExporter putFloatAttr(CharSequence attrName, float fVal) + throws IOException; + + /** + * 属性値を出力する。 + * @param attrName 属性名 + * @param content 属性内容 + * @return this本体 + * @throws IOException 出力エラー + */ + XmlExporter putAttr(CharSequence attrName, CharSequence content) + throws IOException; + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/xml/XmlResourceResolver.java b/src/main/java/jp/sourceforge/mikutoga/xml/XmlResourceResolver.java index 4ea9167..660496a 100644 --- a/src/main/java/jp/sourceforge/mikutoga/xml/XmlResourceResolver.java +++ b/src/main/java/jp/sourceforge/mikutoga/xml/XmlResourceResolver.java @@ -156,6 +156,21 @@ public class XmlResourceResolver } /** + * ローカル版リソース参照解決を登録する。 + * @param lsc ローカル版リソース参照解決 + */ + public void putRedirected(LocalXmlResource lsc){ + URI original = lsc.getOriginalResource(); + if(original == null) return; + + URI local = lsc.getLocalResource(); + + putRedirected(original, local); + + return; + } + + /** * 別リゾルバの登録内容を追加登録する。 * @param other 別リゾルバ */ diff --git a/src/test/java/sample/vmd/DummyHandler.java b/src/test/java/sample/vmd/DummyHandler.java index 73bf7be..f0ee821 100644 --- a/src/test/java/sample/vmd/DummyHandler.java +++ b/src/test/java/sample/vmd/DummyHandler.java @@ -9,19 +9,15 @@ package sample.vmd; import jp.sfjp.mikutoga.bin.parser.MmdFormatException; import jp.sfjp.mikutoga.bin.parser.ParseStage; -import jp.sourceforge.mikutoga.vmd.VmdConst; -import jp.sourceforge.mikutoga.vmd.parser.VmdBasicHandler; -import jp.sourceforge.mikutoga.vmd.parser.VmdCameraHandler; -import jp.sourceforge.mikutoga.vmd.parser.VmdLightingHandler; +import jp.sfjp.mikutoga.vmd.VmdUniq; +import jp.sfjp.mikutoga.vmd.parser.VmdUnifiedHandler; /** * サンプルのハンドラ。 * これはユニットテストではない。 * 必要に応じて要所でデバッガのブレークポイントを設定しておくと便利。 */ -public class DummyHandler implements VmdBasicHandler, - VmdLightingHandler, - VmdCameraHandler { +public class DummyHandler implements VmdUnifiedHandler { public DummyHandler(){ super(); @@ -68,7 +64,7 @@ public class DummyHandler implements VmdBasicHandler, @Override public void vmdModelName(String modelName) throws MmdFormatException{ println("modelname = " + modelName); - println("isStageAct="+VmdConst.isStageActName(modelName)); + println("isStageAct="+VmdUniq.isStageActName(modelName)); } @Override @@ -115,7 +111,7 @@ public class DummyHandler implements VmdBasicHandler, @Override public void vmdMorphMotion(String morphName, int keyFrameNo, float flex){ - if(VmdConst.isBaseMorphName(morphName)) return; + if(VmdUniq.isBaseMorphName(morphName)) return; println(morphName+":"+keyFrameNo+" flex="+flex); return; } diff --git a/src/test/java/sample/vmd/DummyMain.java b/src/test/java/sample/vmd/DummyMain.java index 565e18f..f3c7d3b 100644 --- a/src/test/java/sample/vmd/DummyMain.java +++ b/src/test/java/sample/vmd/DummyMain.java @@ -14,7 +14,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import jp.sfjp.mikutoga.bin.parser.MmdFormatException; -import jp.sourceforge.mikutoga.vmd.parser.VmdParser; +import jp.sfjp.mikutoga.vmd.parser.VmdParser; /** * パーサ利用のサンプルプログラム。 @@ -66,7 +66,8 @@ public class DummyMain { VmdParser parser = new VmdParser(source); setupHandler(parser); - parser.setStrictMode(true); + parser.setIgnoreName(true); + parser.setRedundantCheck(false); try{ parser.parseVmd(); -- 2.11.0