サニタイザ

出典: 謎の百科事典もどき『エンペディア(Enpedia)』
ナビゲーションに移動 検索に移動

サニタイザとは、Web(HTML)環境におけるルーチンのひとつである。WikiWikiWebなどのWebシステムにおいては重要なものではあるが、このあたりはユーザと十分な議論と合意形成が必要なため、あまり話題にはのぼらない。

概要[編集]

WikiWikiWebに代表される HTML ベースのシステムは自前のマシンに Tomcat でも載せて JSP と併用すれば簡単に構築できる。これは個人環境においては不自由はない。
これが LAN 環境において協業のために利用されるようになると、「いちいちHTMLで書くのは手間がかかる」ことから WikiWikiWeb が普及した。ところが、「ウィキ のソースコードにHTMLを貼りつけたらどうなるか?」というのが問題になる。「&(アンパサンド)」「<」「>」「行頭のタブや半角スペース」は、HTML の記述と干渉(あるいは競合)してしまう。
このとき、「文字として受けいれていいのか、それとも HTML のコードとしてそのまんま通しちゃっていいのか」が問題になるわけで、そこを切り分けるのがサニタイザである。
「遠慮なく HTML5 のコードをぶっこむ」というひともいれば、「Wiki記法に即して、編集してくれているユーザ」もいる。そのとき、切り分けてくれるのがサニタイザである。
たとえば、

'&'は「&」に、
'<'は「&lt;」に、
'>'は「&gt;」に、
'"'は「&quot;」に、
'''は「&apos;」に、
' 'は「&nbsp;」に、
'˜'は「&tilde;」に、
'¥'は「&yen;」に、
'˜'は「&tilde;」に、
'©'は「&copy;」に、
書きかえるのがサニタイザの仕事である。このとき、「HTML のコードは 3 タブ、Java のコードは 4 タブ」とかいったルールでコーディングをしていると見た目が相当鬱陶しいことになるので、

  1. まずコードを指定の値で detab して、
  2. それからサニタイズする

という二段構えになる。
ところが、サニタイザの仕様はわかりやすい形で提示されてはいないので、どなたかに交通整理を行なっていただきたいと思っている。[1]

関連項目[編集]

その他[編集]

参考文献[編集]

脚注[編集]

  1. 幸いMediaWikiが存外馬鹿なせいで、変に気を使ったりはしない。おかげで仕事がやりやすく、定跡通りのコードを書けばJavaのコードが無事にインデントされた形で表示され、しかもEclipseにコピペすればちゃんと動きそうだ。これでようやくJavaのソースが晒せる。

サンプルコード[編集]

WebFitter.java[編集]

/**
 *
 * スクロール表示ができるように改造した。
 * http://animaleconomicus.blog106.fc2.com/blog-entry-1359.html に掲載。
 * 旧版が Sanitizer.java。
 */

import java.awt.BorderLayout;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

public class WebFitter extends JFrame {
    private static JTextArea textArea;

    public static void main( String[] args ) {
        new WebFitter().setVisible(true);
    }

    /**
     * コンストラクタだ。
     */
    public WebFitter() {
        this.setSize(400, 350);
        this.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        this.setTitle(&quot;WebFitter&quot;);

        this.addWindowListener(
            new WindowAdapter() {
                /**
                 * ウィンドウがアクティブになった。
                 */
              @Override
                public void windowActivated( WindowEvent evnt ) {
                    String src = &quot;&quot;;
                  try {
                        src = ClipBoardUtility.getTextFromClipBoard();
                  } catch( UnsupportedFlavorException excp ){
                      // 文字列以外のデータが入っていた。
                        excp.printStackTrace();
                    } catch ( IOException excp ) {
                         // エラーになった。
                          excp.printStackTrace();
                     }

                  // ここで Work を呼んでいる
                    // String dst = tools.Work.sanitize(src);
                  String dst = TextConverter.HTML_Sanitize(src);
                    textArea.setText(dst);
                }

                    /**
                     * ウィンドウがアクティブでなくなった。
                     */
                  @Override
                    public void windowDeactivated( WindowEvent e ) {
                        // 現在表示されている内容がクリップされる。
                      ClipBoardUtility.setTextToClipBoard(textArea.getText());
                    }
                }
        );

        /**
         * キーをタイプすると、その内容が入力される。
         * バックスペースと漢字は未対応。
         */
       this.addKeyListener(
           new KeyAdapter() {
             @Override
               public void keyPressed( KeyEvent evnt ) {
                   String msg = textArea.getText() + evnt.getKeyChar();
                   textArea.setText(msg);
               }
           }
       );

       textArea = new JTextArea(5, 20);
       textArea.setLineWrap(true);
       // label.addMouseListener(MyMouseA0);
       textArea.addMouseListener(
           new MouseAdapter(){
             @Override
               public void mouseClicked( MouseEvent evnt ){
                   // ダブルクリックするとクリア
                 // なんか、かぶってる気がするんですが。

                   if ( evnt.getClickCount() == 2 ){
                       textArea.setText(&quot;&quot;);
                   }
               }
           }
       );

       // JFrame である Sanitizer_x に textArea を追加
       // ここにスクロールペインが入るはず。t

       /*
        スクロールの必要がなければ、
        直接 textArea を add することもできる。
       this.add(textArea, BorderLayout.CENTER);
        */
       JScrollPane sp = new JScrollPane(textArea);
       this.add(sp, BorderLayout.CENTER);

       String tmp = &quot;&quot;;
       try {
           tmp = ClipBoardUtility.getTextFromClipBoard();
       } catch( IOException excp ) {
           // 文字列以外のデータが入っていた。
           excp.printStackTrace();
       } catch( UnsupportedFlavorException excp ){
           // 文字列以外のデータが入っていた。
           excp.printStackTrace();
       }
       textArea.setText(&quot;&lt;pre&gt;\n&quot;+ tmp + &quot;&lt;\n&gt;&lt;/pre&gt;\n&quot;);
   }
}

TextConverter.java[編集]

/**
 * HTML 用サニタイザ.
 *
 *
 */
public class TextConverter {

  public static void main( String[] args ) {

  }

  public static String HTML_Sanitize( String src ) {
     StringBuffer buf = new StringBuffer();
        int posx = 0;
        for (int pos = 0; pos < src.length(); pos += 1 ){
            char ch = src.charAt(pos);
            switch(ch) {
            case '\t':
                for (int n = 0; n < (4 - (posx % 4)); n += 1) {
                     buf.append(' ');
                     posx += 1;
                }
                break;
            case '<':
                buf.append("&lt;");
                posx += 1;
                break;
            case '>':
                buf.append("&gt;");
                posx += 1;
                break;
            case '&':
                buf.append("&amp;");
                posx += 1;
                break;
            case '\'':
                buf.append("&apos;");
                posx += 1;
                break;
            case '\"':
              buf.append("&quot;");
                posx += 1;
                break;
           case '\n':
              buf.append('\n');
                posx = 1;
                break;
           default:
              buf.append(ch);
                posx += 1;
                break;
           }
        }
     return ("<pre>\n"+ buf.toString() + "</pre>\n" );
  }
}

ClipBoardUtility.java[編集]

/**
 * main がないので、テスト用のルーチンがどこにあるのかは用件等。
 */
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;

import javax.swing.JFrame;

public class ClipBoardUtility {

	/**
     * クリップボードに入っているテキストの内容を取ってくる。
     * 画像とかだったらいかがなものかと思うが。
     * @return クリップボードに入っているテキスト
     * @throws IOException
     * @throws UnsupportedFlavorException
     */
    public static String getTextFromClipBoard()
    		throws IOException, UnsupportedFlavorException {
    	return getTextFromClipBoard(null);
    }

    /**
     *
     * @param frame
     * @return
     * @throws IOException
     * @throws UnsupportedFlavorException
     */
	public static String getTextFromClipBoard( JFrame frame )
    		throws IOException, UnsupportedFlavorException {
        Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
        Transferable object = clipboard.getContents(frame);
    	String str = (String)object.getTransferData(DataFlavor.stringFlavor);
        return str;
    }


	/**
	 * クリップボードに指定のテキストをセットする。
	 * @param str
	 */
    public static void setTextToClipBoard( String str ){
        Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
    	StringSelection selection = new StringSelection(str);
    	clipboard.setContents(selection, null);
    }
}