OSDN Git Service

SMP面文字代替処理オンオフの選択を可能に
authorOlyutorskii <olyutorskii@users.osdn.me>
Fri, 24 Jun 2016 15:30:22 +0000 (00:30 +0900)
committerOlyutorskii <olyutorskii@users.osdn.me>
Fri, 24 Jun 2016 15:30:22 +0000 (00:30 +0900)
CHANGELOG.txt
src/main/java/jp/sourceforge/jindolf/parser/ContentBuilderUCS2.java
src/main/java/jp/sourceforge/jindolf/parser/EntityConverter.java
src/test/java/jp/sourceforge/jindolf/parser/ContentBuilderUCS2Test.java
src/test/java/jp/sourceforge/jindolf/parser/EntityConverterTest.java

index ef79fd6..3b3e206 100644 (file)
@@ -4,6 +4,9 @@
 JinParser 変更履歴
 
 
+1.408.7-SNAPSHOT (2016-XX-XX)
+    ・SMP面文字代替処理のオンオフを可能に。
+
 1.408.6 (2016-06-17)
     ・必須環境をJRE7に引き上げ。
     ・Mavenプラグイン更新。
index c1f9965..5ab7832 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * content builder for UTF-8 (UCS2 only)
+ * content builder for UTF-8
  *
  * License : The MIT License
  * Copyright(c) 2010 olyutorskii
@@ -11,7 +11,6 @@ package jp.sourceforge.jindolf.parser;
  * "UTF-8"エンコーディング用デコードハンドラ。
  * {@link StreamDecoder}からの通知に従い、
  * {@link DecodedContent}へとデコードする。
- * UCS-4はUTF-16エラー扱い。
  */
 public class ContentBuilderUCS2 extends ContentBuilder{
 
@@ -42,20 +41,6 @@ public class ContentBuilderUCS2 extends ContentBuilder{
 
 
     /**
-     * サロゲートペア文字(上位,下位)をUTF-16BEバイト列に変換する。
-     * @param ch 文字
-     * @return UTF-8バイト列
-     */
-    public static byte[] charToUTF16(char ch){
-        byte[] result = new byte[2];
-        result[0] = (byte) (ch >> 8);
-        result[1] = (byte) (ch & 0xff);
-
-        return result;
-    }
-
-
-    /**
      * デコード処理の初期化下請。
      */
     private void initImpl(){
@@ -81,36 +66,7 @@ public class ContentBuilderUCS2 extends ContentBuilder{
     public void charContent(CharSequence seq)
             throws DecodeException{
         flushError();
-
-        int length = seq.length();
-        int copyDone = 0;
-
-        for(int pos = 0; pos < length; pos++){
-            char ch = seq.charAt(pos);
-
-            if(    ! Character.isHighSurrogate(ch)
-                && ! Character.isLowSurrogate (ch) ){
-                continue;
-            }
-
-            if(copyDone < pos){
-                CharSequence chopped = seq.subSequence(copyDone, pos);
-                getContent().append(chopped);
-            }
-
-            copyDone = pos + 1;
-
-            byte[] barr = charToUTF16(ch);
-            for(byte bval : barr){
-                getContent().addDecodeError(bval);
-            }
-        }
-
-        if(copyDone < length){
-            CharSequence chopped = seq.subSequence(copyDone, length);
-            getContent().append(chopped);
-        }
-
+        getContent().append(seq);
         return;
     }
 
index 6f84d28..ae18d4c 100644 (file)
@@ -13,55 +13,69 @@ import java.util.regex.Pattern;
 /**
  * 人狼BBSで用いられる4種類のXHTML文字実体参照の
  * 解決を伴う{@link DecodedContent}の切り出しを行う。
- * 文字実体参照は{@code &gt; &lt; &quot; &amp;}が対象。
- * U+005C(バックスラッシュ)をU+00A5(円通貨)に直す処理も行われる。
+ *
+ * <p>文字実体参照は{@code &gt; &lt; &quot; &amp;}が対象。
+ *
+ * <p>U+005C(バックスラッシュ)をU+00A5(円通貨)に直す処理も行われる。
  * ※ 人狼BBSはShift_JIS(⊃JISX0201)で運営されているので、
  * バックスラッシュは登場しないはず。
  * ※ が、バックスラッシュを生成するShift_JISデコーダは存在する。
- * マルチスレッドには非対応。
+ *
+ * <p>指示によりサロゲートペア上位下位の並びを
+ * 単一疑問符?に直す処理も可能。
+ * {@link java.lang.Character#MIN_SUPPLEMENTARY_CODE_POINT}
+ * {@link java.lang.Character#MAX_CODE_POINT}
+ *
+ * <p>マルチスレッドには非対応。
  */
 public class EntityConverter{
 
-    private static final String[][] XCHG_TABLE = {
-        {"&gt;",   ">"},
-        {"&lt;",   "<"},
-        {"&quot;", "\""},
-        {"&amp;",  "&"},
-        {"\u005c\u005c", "\u00a5"},
-    };
-
-    private static final Pattern XCHG_PATTERN;
-
-    static{
-        StringBuilder regex = new StringBuilder();
-        for(String[] xchg : XCHG_TABLE){
-            String xchgFrom = xchg[0];
-            if(regex.length() > 0) regex.append('|');
-            regex.append('(')
-                 .append(Pattern.quote(xchgFrom))
-                 .append(')');
-            assert xchgFrom.indexOf(DecodedContent.ALTCHAR) < 0;
-        }
-        XCHG_PATTERN = Pattern.compile(regex.toString());
-    }
+    private static final char   DQ_CH = '"';
+    private static final String DQ_STR = Character.toString(DQ_CH);
+    private static final String YEN_STR = "\u00a5";
+
+    private static final char   BS_CH = '\u005c\u005c';
+    private static final String BS_STR = Character.toString(BS_CH);
+    private static final String BS_PATTERN = BS_STR + BS_STR;
+
+    private static final String UCS4_PATTERN = "[\\x{10000}-\\x{10ffff}]";
+
+    private static final RegexRep[] VALUES_CACHE = RegexRep.values();
+
+
+    private final Matcher matcher = RegexRep.buildMatcher();
+    private final boolean replaceSmp;
 
-    private final Matcher matcher = XCHG_PATTERN.matcher("");
 
     /**
      * コンストラクタ。
+     * SMP面文字の代替処理は行われない。
      */
     public EntityConverter(){
+        this(false);
+        return;
+    }
+
+    /**
+     * コンストラクタ。
+     * @param replaceSmp SMP面文字を代替処理するならtrue
+     */
+    public EntityConverter(boolean replaceSmp){
         super();
+        this.replaceSmp = replaceSmp;
         return;
     }
 
+
     /**
      * 実体参照の変換を行う。
      * @param content 変換元文書
      * @return 切り出された変換済み文書
      */
     public DecodedContent convert(DecodedContent content){
-        return append(null, content, 0, content.length());
+        int startPos = 0;
+        int endPos   = content.length();
+        return append(null, content, startPos, endPos);
     }
 
     /**
@@ -73,7 +87,9 @@ public class EntityConverter{
      */
     public DecodedContent convert(DecodedContent content, SeqRange range)
             throws IndexOutOfBoundsException{
-        return append(null, content, range.getStartPos(), range.getEndPos());
+        int startPos = range.getStartPos();
+        int endPos   = range.getEndPos();
+        return append(null, content, startPos, endPos);
     }
 
     /**
@@ -100,7 +116,9 @@ public class EntityConverter{
     public DecodedContent  append(DecodedContent target,
                                    DecodedContent content)
             throws IndexOutOfBoundsException{
-        return append(target, content, 0, content.length());
+        int startPos = 0;
+        int endPos   = content.length();
+        return append(target, content, startPos, endPos);
     }
 
     /**
@@ -115,8 +133,9 @@ public class EntityConverter{
                                    DecodedContent content,
                                    SeqRange range )
             throws IndexOutOfBoundsException{
-        return append(target, content,
-                      range.getStartPos(), range.getEndPos());
+        int startPos = range.getStartPos();
+        int endPos   = range.getEndPos();
+        return append(target, content, startPos, endPos);
     }
 
     /**
@@ -140,7 +159,8 @@ public class EntityConverter{
 
         DecodedContent result;
         if(target == null){
-            result = new DecodedContent(endPos - startPos);
+            int length = endPos - startPos;
+            result = new DecodedContent(length);
         }else{
             result = target;
         }
@@ -148,28 +168,115 @@ public class EntityConverter{
         this.matcher.reset(content.getRawContent());
         this.matcher.region(startPos, endPos);
 
-        int lastPos = startPos;
+        int copiedPos = startPos;
         while(this.matcher.find()){
-            int group;
+            int group = -1;
             int matchStart = -1;
-            for(group = 1; group <= XCHG_TABLE.length; group++){
+            String altTxt = "";
+            for(RegexRep rr : VALUES_CACHE){
+                group = rr.getGroupNo();
                 matchStart = this.matcher.start(group);
-                if(matchStart >= 0) break;
+                if(matchStart >= 0){
+                    if(rr == RegexRep.UCS4 &&  ! this.replaceSmp){
+                        altTxt = this.matcher.group(group);
+                    }else{
+                        altTxt = rr.getAltTxt();
+                    }
+                    break;
+                }
             }
+            assert group >= 1;
             int matchEnd = this.matcher.end(group);
 
-            result.append(content, lastPos, matchStart);
-
-            String toStr = XCHG_TABLE[group - 1][1];
-            result.append(toStr);
+            result.append(content, copiedPos, matchStart);
+            result.append(altTxt);
 
-            lastPos = matchEnd;
+            copiedPos = matchEnd;
         }
-        result.append(content, lastPos, endPos);
+        result.append(content, copiedPos, endPos);
 
         this.matcher.reset("");
 
         return result;
     }
 
+
+    /**
+     * 文字列置換リスト。
+     */
+    private static enum RegexRep{
+
+        GT   ("&gt;",       ">"),
+        LT   ("&lt;",       "<"),
+        AMP  ("&amp;",      "&"),
+        QUAT ("&quot;",     DQ_STR),
+        BS   (BS_PATTERN,   YEN_STR),
+        UCS4 (UCS4_PATTERN, "?"),
+        ;
+
+
+        private final String regex;
+        private final String altTxt;
+
+
+        /**
+         * コンストラクタ。
+         * @param regex 置換元パターン正規表現
+         * @param altTxt 置換文字列。
+         */
+        private RegexRep(String regex, String altTxt){
+            this.regex = regex;
+            this.altTxt = altTxt;
+            return;
+        }
+
+
+        /**
+         * 全正規表現をOR連結したパターンを生成する。
+         * @return パターン
+         */
+        private static Pattern buildPattern(){
+            StringBuilder orRegex = new StringBuilder();
+
+            for(RegexRep rr : values()){
+                if(orRegex.length() > 0) orRegex.append('|');
+                orRegex.append('(');
+                orRegex.append(rr.regex);
+                orRegex.append(')');
+            }
+
+            Pattern result = Pattern.compile(orRegex.toString());
+            return result;
+        }
+
+        /**
+         * マッチャを生成する。
+         * @return マッチャ
+         */
+        private static Matcher buildMatcher(){
+            Pattern pattern = buildPattern();
+            Matcher result = pattern.matcher("");
+            return result;
+        }
+
+
+        /**
+         * 置換文字列を返す。
+         * @return 置換文字列
+         */
+        private String getAltTxt(){
+            return this.altTxt;
+        }
+
+        /**
+         * パターン内において占めるグループ番号を返す。
+         * @return グループ番号
+         */
+        private int getGroupNo(){
+            int group = ordinal() + 1;
+            return group;
+        }
+
+    }
+
 }
index 67d0d48..5cf6504 100644 (file)
@@ -315,8 +315,6 @@ public class ContentBuilderUCS2Test {
         byte[] bdata;
         InputStream is;
         DecodedContent content;
-        List<DecodeErrorInfo> errList;
-        DecodeErrorInfo einfo;
 
         cd = cs.newDecoder();
         decoder = new StreamDecoder(cd);
@@ -327,113 +325,8 @@ public class ContentBuilderUCS2Test {
         decoder.decode(is);
         content = cb.getContent();
 
-//        assertEquals(7, content.length());
-//        assertEquals("A????\udc11B", content.toString());
-        assertEquals(6, content.length());
-        assertEquals("A????B", content.toString());
-        assertTrue(content.hasDecodeError());
-        errList = content.getDecodeErrorList();
-        assertEquals(4, errList.size());
-        einfo = errList.get(0);
-        assertFalse(einfo.has2nd());
-        assertEquals((byte)0xd8, einfo.getRawByte1st());
-        assertEquals(1, einfo.getCharPosition());
-        einfo = errList.get(1);
-        assertFalse(einfo.has2nd());
-        assertEquals((byte)0x3d, einfo.getRawByte1st());
-        assertEquals(2, einfo.getCharPosition());
-        einfo = errList.get(2);
-        assertFalse(einfo.has2nd());
-        assertEquals((byte)0xdc, einfo.getRawByte1st());
-        assertEquals(3, einfo.getCharPosition());
-        einfo = errList.get(3);
-        assertFalse(einfo.has2nd());
-        assertEquals((byte)0x11, einfo.getRawByte1st());
-        assertEquals(4, einfo.getCharPosition());
-
-
-        cd = cs.newDecoder();
-        decoder = new StreamDecoder(cd);
-        cb = new ContentBuilderUCS2();
-        decoder.setDecodeHandler(cb);
-        bdata = byteArray("d83d:dc11:0042");
-        is = new ByteArrayInputStream(bdata);
-        decoder.decode(is);
-        content = cb.getContent();
-
-        assertEquals(5, content.length());
-        assertEquals("????B", content.toString());
-        assertTrue(content.hasDecodeError());
-        errList = content.getDecodeErrorList();
-        assertEquals(4, errList.size());
-        einfo = errList.get(0);
-        assertFalse(einfo.has2nd());
-        assertEquals((byte)0xd8, einfo.getRawByte1st());
-        assertEquals(0, einfo.getCharPosition());
-        einfo = errList.get(1);
-        assertFalse(einfo.has2nd());
-        assertEquals((byte)0x3d, einfo.getRawByte1st());
-        assertEquals(1, einfo.getCharPosition());
-        einfo = errList.get(2);
-        assertFalse(einfo.has2nd());
-        assertEquals((byte)0xdc, einfo.getRawByte1st());
-        assertEquals(2, einfo.getCharPosition());
-        einfo = errList.get(3);
-        assertFalse(einfo.has2nd());
-        assertEquals((byte)0x11, einfo.getRawByte1st());
-        assertEquals(3, einfo.getCharPosition());
-
-
-        cd = cs.newDecoder();
-        decoder = new StreamDecoder(cd);
-        cb = new ContentBuilderUCS2();
-        decoder.setDecodeHandler(cb);
-        bdata = byteArray("0041:d83d:dc11");
-        is = new ByteArrayInputStream(bdata);
-        decoder.decode(is);
-        content = cb.getContent();
-
-        assertEquals(5, content.length());
-        assertEquals("A????", content.toString());
-        assertTrue(content.hasDecodeError());
-        errList = content.getDecodeErrorList();
-        assertEquals(4, errList.size());
-        einfo = errList.get(0);
-        assertFalse(einfo.has2nd());
-        assertEquals((byte)0xd8, einfo.getRawByte1st());
-        assertEquals(1, einfo.getCharPosition());
-        einfo = errList.get(1);
-        assertFalse(einfo.has2nd());
-        assertEquals((byte)0x3d, einfo.getRawByte1st());
-        assertEquals(2, einfo.getCharPosition());
-        einfo = errList.get(2);
-        assertFalse(einfo.has2nd());
-        assertEquals((byte)0xdc, einfo.getRawByte1st());
-        assertEquals(3, einfo.getCharPosition());
-        einfo = errList.get(3);
-        assertFalse(einfo.has2nd());
-        assertEquals((byte)0x11, einfo.getRawByte1st());
-        assertEquals(4, einfo.getCharPosition());
-
-
-        return;
-    }
-
-
-    /**
-     * Test of charToUTF8 method, of class ContentBuilderUCS2.
-     */
-    @Test
-    public void testCharToUTF16(){
-        System.out.println("charToUTF16");
-
-        char ch;
-        byte[] result;
-
-        ch = '\ud844';
-        result = ContentBuilderUCS2.charToUTF16(ch);
-
-        assertEquals(2, result.length);
+        assertEquals(4, content.length());
+        assertEquals("A\ud83d\udc11B", content.toString());
 
         return;
     }
index 1a2b1b8..ed5e7e8 100644 (file)
@@ -111,6 +111,44 @@ public class EntityConverterTest {
         result = converter.convert(from, range);
         assertEquals("bcd", result.toString());
 
+        from = new DecodedContent();
+        from.append("abcde");
+        try{
+            converter.convert(from, 4, 1);
+            fail();
+        }catch(IndexOutOfBoundsException e){
+            // OK
+        }
+
+        from = new DecodedContent();
+        from.append("abcde");
+        try{
+            converter.convert(from, -1, 4);
+            fail();
+        }catch(IndexOutOfBoundsException e){
+            // OK
+        }
+
+        from = new DecodedContent();
+        from.append("abcde");
+        try{
+            converter.convert(from, 1, 6);
+            fail();
+        }catch(IndexOutOfBoundsException e){
+            // OK
+        }
+
+        from = new DecodedContent();
+        from.append("a\ud83d\udc11b"); // 🐑
+        result = converter.convert(from);
+        assertEquals("a\ud83d\udc11b", result.toString());
+
+        from = new DecodedContent();
+        from.append("a\ud83d\udc11b"); // 🐑
+        EntityConverter repConverter = new EntityConverter(true);
+        result = repConverter.convert(from);
+        assertEquals("a?b", result.toString());
+
         return;
     }