/*
 * This software is distributed under following license based on modified BSD
 * style license.
 * ----------------------------------------------------------------------
 * 
 * Copyright 2009 The Nimbus2 Project. All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer. 
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE NIMBUS PROJECT ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
 * NO EVENT SHALL THE NIMBUS PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * The views and conclusions contained in the software and documentation are
 * those of the authors and should not be interpreted as representing official
 * policies, either expressed or implied, of the Nimbus2 Project.
 */
package jp.ossc.nimbus.service.journal.editor;

import java.io.Serializable;
import java.util.*;
import javax.servlet.ServletRequest;
import javax.servlet.http.*;

import jp.ossc.nimbus.service.journal.EditorFinder;

/**
 * HttpServletRequestオブジェクトをMapフォーマットするエディタ。<p>
 * このエディタによって編集されたMapは、{@link ServletRequestMapJournalEditorService}の持つMap構造に加えて、以下の構造を持つ。<br>
 * <table broder="1">
 *   <tr bgcolor="#CCCCFF"><th colspan="2">キー</th><th colspan="5">値</th></tr>
 *   <tr bgcolor="#CCCCFF"><th>型</th><th>内容</th><th>型</th><th colspan="4">内容</th></tr>
 *   <tr bgcolor="#CCCCFF"><th>型</th><th>内容</th><th>型</th><th>内容</th></tr>
 *   <tr><td>java.lang.String</td><td>{@link #AUTH_TYPE_KEY}</td><td>java.lang.String</td><td colspan="4">認証タイプ名</td></tr>
 *   <tr><td>java.lang.String</td><td>{@link #REMOTE_USER_KEY}</td><td>java.lang.String</td><td colspan="4">認証されたリモートのユーザ名</td></tr>
 *   <tr><td>java.lang.String</td><td>{@link #USER_PRINCIPAL_KEY}</td><td>java.lang.String</td><td colspan="4">認証されたユーザの主体</td></tr>
 *   <tr><td>java.lang.String</td><td>{@link #REQUEST_URL_KEY}</td><td>java.lang.String</td><td colspan="4">リクエストされたURL</td></tr>
 *   <tr><td>java.lang.String</td><td>{@link #REQUEST_URI_KEY}</td><td>java.lang.String</td><td colspan="4">リクエストされたURLのプロトコル名からクエリ文字列までの部分</td></tr>
 *   <tr><td>java.lang.String</td><td>{@link #SERVLET_PATH_KEY}</td><td>java.lang.String</td><td colspan="4">リクエストされたURLのサーブレットを呼び出すURL部分</td></tr>
 *   <tr><td>java.lang.String</td><td>{@link #SESSION_ID_KEY}</td><td>java.lang.String</td><td colspan="4">セッションID</td></tr>
 *   <tr><td>java.lang.String</td><td>{@link #SESSION_ID_FROM_COOKIE_KEY}</td><td>java.lang.Boolean</td><td colspan="4">セッションIDがCookieから取得されたかを示すフラグ</td></tr>
 *   <tr><td>java.lang.String</td><td>{@link #SESSION_ID_FROM_URL_KEY}</td><td>java.lang.Boolean</td><td colspan="4">セッションIDがURLから取得されたかを示すフラグ</td></tr>
 *   <tr><td>java.lang.String</td><td>{@link #HTTP_METHOD_KEY}</td><td>java.lang.String</td><td colspan="4">HTTPメソッド名</td></tr>
 *   <tr><td rowspan="3">java.lang.String</td><td rowspan="3">{@link #HTTP_HEADERS_KEY}</td><td rowspan="3">java.util.Map</td><td colspan="4">HTTPヘッダのマップ</td></tr>
 *   <tr><td>java.lang.String</td><td>HTTPヘッダのキー名</td><td>java.util.Enumeration</td><td>HTTPヘッダの値の列挙</td></tr>
 *   <tr><td rowspan="3">java.lang.String</td><td rowspan="3">{@link #COOKIES_KEY}</td><td rowspan="3">java.util.Map</td><td colspan="4">Cookieのマップ</td></tr>
 *   <tr><td>java.lang.String</td><td>Cookieのキー名</td><td>java.lang.String</td><td>Cookieの値</td></tr>
 *   <tr><td>java.lang.String</td><td>{@link #CONTEXT_PATH_KEY}</td><td>java.lang.String</td><td colspan="4">コンテキストを示すリクエストURIの一部</td></tr>
 *   <tr><td>java.lang.String</td><td>{@link #PATH_INFO_KEY}</td><td>java.lang.String</td><td colspan="4">クライアントがURLに関連付けて送信した追加のパス情報</td></tr>
 *   <tr><td>java.lang.String</td><td>{@link #PATH_TRAN_KEY}</td><td>java.lang.String</td><td colspan="4">サーブレット名の後でクエリ文字列の前にある追加パス情報を実際のパスに変換したもの</td></tr>
 *   <tr><td>java.lang.String</td><td>{@link #QUERY_STRING_KEY}</td><td>java.lang.String</td><td colspan="4">リクエストURLのパスの後ろに含まれているクエリ文字列</td></tr>
 * </table>
 * 但し、出力しないように設定されているものや、元のHttpServletRequestに含まれていなかった情報、J2EEのバージョンによって取得できない情報は含まれない。<br>
 * 
 * @author M.Takata
 */
public class HttpServletRequestMapJournalEditorService
 extends ServletRequestMapJournalEditorService
 implements HttpServletRequestMapJournalEditorServiceMBean, Serializable{
    
    private static final long serialVersionUID = -5560369100004636591L;
    
    private String[] secretHeaders;
    protected Set<String> secretHeaderSet;
    private String[] enabledHeaders;
    protected Set<String> enabledHeaderSet;
    private String[] disabledHeaders;
    protected Set<String> disabledHeaderSet;
    private String[] secretCookies;
    protected Set<String> secretCookieSet;
    private String[] enabledCookies;
    protected Set<String> enabledCookieSet;
    private String[] disabledCookies;
    protected Set<String> disabledCookieSet;
    
    @Override
    public void setSecretHeaders(String[] names){
        secretHeaders = names;
    }
    
    @Override
    public String[] getSecretHeaders(){
        return secretHeaders;
    }
    
    @Override
    public void setEnabledHeaders(String[] names){
        enabledHeaders = names;
    }
    
    @Override
    public String[] getEnabledHeaders(){
        return enabledHeaders;
    }
    
    @Override
    public void setDisabledHeaders(String[] names){
        disabledHeaders = names;
    }
    
    @Override
    public String[] getDisabledHeaders(){
        return disabledHeaders;
    }
    
    @Override
    public void setSecretCookies(String[] names){
        secretCookies = names;
    }
    
    @Override
    public String[] getSecretCookies(){
        return secretCookies;
    }
    
    @Override
    public void setEnabledCookies(String[] names){
        enabledCookies = names;
    }
    
    @Override
    public String[] getEnabledCookies(){
        return enabledCookies;
    }
    
    @Override
    public void setDisabledCookies(String[] names){
        disabledCookies = names;
    }
    
    @Override
    public String[] getDisabledCookies(){
        return disabledCookies;
    }
    
    @Override
    public void createService(){
        super.createService();
        secretHeaderSet = new HashSet<String>();
        enabledHeaderSet = new HashSet<String>();
        disabledHeaderSet = new HashSet<String>();
        secretCookieSet = new HashSet<String>();
        enabledCookieSet = new HashSet<String>();
        disabledCookieSet = new HashSet<String>();
    }
    
    @Override
    public void startService(){
        super.startService();
        if(secretHeaders != null){
            for(int i = 0; i < secretHeaders.length; i++){
                secretHeaderSet.add(secretHeaders[i]);
            }
        }
        if(enabledHeaders != null){
            for(int i = 0; i < enabledHeaders.length; i++){
                enabledHeaderSet.add(enabledHeaders[i]);
            }
        }
        if(disabledHeaders != null){
            for(int i = 0; i < disabledHeaders.length; i++){
                disabledHeaderSet.add(disabledHeaders[i]);
            }
        }
        if(secretCookies != null){
            for(int i = 0; i < secretCookies.length; i++){
                secretCookieSet.add(secretCookies[i]);
            }
        }
        if(enabledCookies != null){
            for(int i = 0; i < enabledCookies.length; i++){
                enabledCookieSet.add(enabledCookies[i]);
            }
        }
        if(disabledCookies != null){
            for(int i = 0; i < disabledCookies.length; i++){
                disabledCookieSet.add(disabledCookies[i]);
            }
        }
    }
    
    @Override
    public void stopService(){
        super.stopService();
        secretHeaderSet.clear();
        enabledHeaderSet.clear();
        disabledHeaderSet.clear();
        secretCookieSet.clear();
        enabledCookieSet.clear();
        disabledCookieSet.clear();
    }
    
    @Override
    public void destroyService(){
        super.destroyService();
        secretHeaderSet = null;
        enabledHeaderSet = null;
        disabledHeaderSet = null;
        secretCookieSet = null;
        enabledCookieSet = null;
        disabledCookieSet = null;
    }
    
    /**
     * ジャーナルとして与えられたHttpServletRequest型の情報をジャーナルとして出力するMap情報に変換する。<br>
     * 
     * @param finder 適切なJournalEditorを提供するEditorFinder
     * @param key ジャーナルのキー情報
     * @param value ジャーナル情報
     * @return ジャーナルとして出力するMap情報
     */
    @Override
    protected Map<String, Object> toMap(EditorFinder finder, String key, Object value){
        final ServletRequest request = (ServletRequest)value;
        final Map<String, Object> result = super.toMap(finder, key, request);
        
        final HttpServletRequest httpRequest = (HttpServletRequest)request;
        makeRequestURLFormat(finder, key, httpRequest, result);
        makeRequestURIFormat(finder, key, httpRequest, result);
        makeServletPathFormat(finder, key, httpRequest, result);
        makeContextPathFormat(finder, key, httpRequest, result);
        makePathInfoFormat(finder, key, httpRequest, result);
        makePathTranslatedFormat(finder, key, httpRequest, result);
        makeQueryStringFormat(finder, key, httpRequest, result);
        makeSessionIDFormat(finder, key, httpRequest, result);
        makeIsRequestedSessionIdFromCookieFormat(
            finder,
            key,
            httpRequest,
            result
        );
        makeIsRequestedSessionIdFromURLFormat(
            finder,
            key,
            httpRequest,
            result
        );
        makeMethodFormat(finder, key, httpRequest, result);
        makeAuthTypeFormat(finder, key, httpRequest, result);
        makeRemoteUserFormat(finder, key, httpRequest, result);
        makeUserPrincipalFormat(finder, key, httpRequest, result);
        makeHeadersFormat(finder, key, httpRequest, result);
        makeCookiesFormat(finder, key, httpRequest, result);
        
        return result;
    }
    
    protected Map<String, Object> makeAuthTypeFormat(
        EditorFinder finder,
        String key,
        HttpServletRequest request,
        Map<String, Object> map
    ){
        putMap(map, AUTH_TYPE_KEY, request.getAuthType());
        return map;
    }
    
    protected Map<String, Object> makeRemoteUserFormat(
        EditorFinder finder,
        String key,
        HttpServletRequest request,
        Map<String, Object> map
    ){
        putMap(map, REMOTE_USER_KEY, request.getRemoteUser());
        return map;
    }
    
    protected Map<String, Object> makeUserPrincipalFormat(
        EditorFinder finder,
        String key,
        HttpServletRequest request,
        Map<String, Object> map
    ){
        putMap(map, USER_PRINCIPAL_KEY, request.getUserPrincipal());
        return map;
    }
    
    protected Map<String, Object> makeRequestURLFormat(
        EditorFinder finder,
        String key,
        HttpServletRequest request,
        Map<String, Object> map
    ){
        putMap(map, REQUEST_URL_KEY, request.getRequestURL());
        return map;
    }
    
    protected Map<String, Object> makeRequestURIFormat(
        EditorFinder finder,
        String key,
        HttpServletRequest request,
        Map<String, Object> map
    ){
        putMap(map, REQUEST_URI_KEY, request.getRequestURI());
        return map;
    }
    
    protected Map<String, Object> makeServletPathFormat(
        EditorFinder finder,
        String key,
        HttpServletRequest request,
        Map<String, Object> map
    ){
        putMap(map, SERVLET_PATH_KEY, request.getServletPath());
        return map;
    }
    
    protected Map<String, Object> makeContextPathFormat(
        EditorFinder finder,
        String key,
        HttpServletRequest request,
        Map<String, Object> map
    ){
        putMap(map, CONTEXT_PATH_KEY, request.getContextPath());
        return map;
    }
    
    protected Map<String, Object> makePathInfoFormat(
        EditorFinder finder,
        String key,
        HttpServletRequest request,
        Map<String, Object> map
    ){
        putMap(map, PATH_INFO_KEY, request.getPathInfo());
        return map;
    }
    
    protected Map<String, Object> makePathTranslatedFormat(
        EditorFinder finder,
        String key,
        HttpServletRequest request,
        Map<String, Object> map
    ){
        putMap(map, PATH_TRAN_KEY, request.getPathTranslated());
        return map;
    }
    
    protected Map<String, Object> makeQueryStringFormat(
        EditorFinder finder,
        String key,
        HttpServletRequest request,
        Map<String, Object> map
    ){
        putMap(map, QUERY_STRING_KEY, request.getQueryString());
        return map;
    }
    
    protected Map<String, Object> makeSessionIDFormat(
        EditorFinder finder,
        String key,
        HttpServletRequest request,
        Map<String, Object> map
    ){
        putMap(map, SESSION_ID_KEY, request.getRequestedSessionId());
        return map;
    }
    
    protected Map<String, Object> makeIsRequestedSessionIdFromCookieFormat(
        EditorFinder finder,
        String key,
        HttpServletRequest request,
        Map<String, Object> map
    ){
        putMap(map, 
            SESSION_ID_FROM_COOKIE_KEY,
            request.isRequestedSessionIdFromCookie() ? Boolean.TRUE : Boolean.FALSE
        );
        return map;
    }
    
    protected Map<String, Object> makeIsRequestedSessionIdFromURLFormat(
        EditorFinder finder,
        String key,
        HttpServletRequest request,
        Map<String, Object> map
    ){
        putMap(map, 
            SESSION_ID_FROM_URL_KEY,
            request.isRequestedSessionIdFromURL() ? Boolean.TRUE : Boolean.FALSE
        );
        return map;
    }
    
    protected Map<String, Object> makeMethodFormat(
        EditorFinder finder,
        String key,
        HttpServletRequest request,
        Map<String, Object> map
    ){
        putMap(map, HTTP_METHOD_KEY, request.getMethod());
        return map;
    }
    
    @SuppressWarnings("unchecked")
    protected Map<String, Object> makeHeadersFormat(
        EditorFinder finder,
        String key,
        HttpServletRequest request,
        Map<String, Object> map
    ){
        if(!isOutputMapKey(HTTP_HEADERS_KEY)){
            return map;
        }
        if(isSecretMapKey(HTTP_HEADERS_KEY)){
            putMap(map, HTTP_HEADERS_KEY, null);
            return map;
        }
        final Enumeration<String> headerNames = request.getHeaderNames();
        if(!headerNames.hasMoreElements()){
            return map;
        }
        final Map<String, Object> subMap = new LinkedHashMap<String, Object>();
        while(headerNames.hasMoreElements()){
            final String name = (String)headerNames.nextElement();
            if(!disabledHeaderSet.isEmpty()
                 && disabledHeaderSet.contains(name)){
                continue;
            }
            if(!enabledHeaderSet.isEmpty()
                 && !enabledHeaderSet.contains(name)){
                continue;
            }
            if(secretHeaderSet.contains(name)){
                subMap.put(name, getSecretString());
            }else{
                final Enumeration<Object> headers = request.getHeaders(name);
                if(headers != null){
                    final List<Object> values = new ArrayList<Object>();
                    while(headers.hasMoreElements()){
                        values.add(headers.nextElement());
                    }
                    subMap.put(
                        name,
                        makeObjectFormat(finder, key, values)
                    );
                }
            }
        }
        putMap(map, HTTP_HEADERS_KEY, subMap);
        return map;
    }
    
    protected Map<String, Object> makeCookiesFormat(
        EditorFinder finder,
        String key,
        HttpServletRequest request,
        Map<String, Object> map
    ){
        if(!isOutputMapKey(COOKIES_KEY)){
            return map;
        }
        if(isSecretMapKey(COOKIES_KEY)){
            putMap(map, COOKIES_KEY, null);
            return map;
        }
        final Cookie[] cookies = request.getCookies();
        if(cookies == null || cookies.length == 0){
            return map;
        }
        final Map<String, Object> subMap = new LinkedHashMap<String, Object>();
        for(int i = 0; i < cookies.length; i++){
            final String name = (String)cookies[i].getName();
            if(!disabledCookieSet.isEmpty()
                 && disabledCookieSet.contains(name)){
                continue;
            }
            if(!enabledCookieSet.isEmpty()
                 && !enabledCookieSet.contains(name)){
                continue;
            }
            if(secretCookieSet.contains(name)){
                subMap.put(name, getSecretString());
            }else{
                subMap.put(
                    name,
                    makeObjectFormat(finder, key, cookies[i].getValue())
                );
            }
        }
        putMap(map, COOKIES_KEY, subMap);
        return map;
    }
}
