/*
 * 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;

import java.util.*;
import java.util.regex.*;

import jp.ossc.nimbus.core.*;
import jp.ossc.nimbus.beans.*;

/**
 * 正規表現クラス名マッピングEditorFinderサービス。<p>
 *
 * @author M.Takata
 */
public class RegexClassMappedEditorFinderService extends ServiceBase
 implements EditorFinder, RegexClassMappedEditorFinderServiceMBean{
    
    private static final long serialVersionUID = 8606953387609440972L;
    
    private ServiceName parentEditorFinderServiceName;
    private EditorFinder parentEditorFinder;
    private Map<String, Object> journalEditorMapping;
    private Map<String, Map<String, JournalEditor>> editorRegexMapping;
    private Map<String, Pattern> namePatternMapping;
    
    public void setParentEditorFinderServiceName(ServiceName name){
        parentEditorFinderServiceName = name;
    }
    public ServiceName getParentEditorFinderServiceName(){
        return parentEditorFinderServiceName;
    }
    
    public void setJournalEditorMapping(Map<String, Object> map){
        journalEditorMapping = map;
    }
    public Map<String, Object> getJournalEditorMapping(){
        return journalEditorMapping;
    }
    
    /**
     * 親EditorFinderを設定する。<p>
     *
     * @param finder 親EditorFinder
     */
    public void setEditorFinder(EditorFinder finder) {
        this.parentEditorFinder = finder;
    }
    
    public void createService() throws Exception{
        editorRegexMapping = new HashMap<String, Map<String, JournalEditor>>();
        namePatternMapping = new LinkedHashMap<String, Pattern>();
    }
    
    public void startService() throws Exception{
        if(parentEditorFinderServiceName != null){
            parentEditorFinder = ServiceManagerFactory
                .getServiceObject(parentEditorFinderServiceName);
        }
        if(journalEditorMapping == null || journalEditorMapping.size() == 0){
            throw new IllegalArgumentException("JournalEditorMapping must be specified.");
        }
        final ServiceNameEditor editor = new ServiceNameEditor();
        editor.setServiceManagerName(getServiceManagerName());
        final Iterator<String> keyStrs = journalEditorMapping.keySet().iterator();
        while(keyStrs.hasNext()){
            final String keyStr = keyStrs.next();
            String key = null;
            String className = null;
            final int index = keyStr.indexOf(',');
            if(index == -1){
                className = keyStr;
            }else{
                className = keyStr.substring(0, index);
                if(index != keyStr.length() - 1){
                    key = keyStr.substring(index + 1);
                }
            }
            
            Object value = journalEditorMapping.get(keyStr);
            JournalEditor journalEditor = null;
            if(value instanceof String){
                String nameStr = (String)value;
                editor.setAsText(nameStr);
                final ServiceName name = (ServiceName)editor.getValue();
                journalEditor = ServiceManagerFactory.getServiceObject(name);
            }else if(value instanceof JournalEditor){
                journalEditor = (JournalEditor)value;
            }
            
            Pattern classNamePattern = Pattern.compile(className);
            namePatternMapping.put(className, classNamePattern);
            Map<String, JournalEditor> keyEditorMap = editorRegexMapping.get(className);
            if(keyEditorMap == null){
                keyEditorMap = new HashMap<String, JournalEditor>();
                editorRegexMapping.put(className, keyEditorMap);
            }
            keyEditorMap.put(key, journalEditor);
        }
    }
    
    public void stopService() throws Exception{
        editorRegexMapping.clear();
        namePatternMapping.clear();
    }
    
    public void destroyService() throws Exception{
        editorRegexMapping = null;
        namePatternMapping = null;
    }
    
    public JournalEditor findEditor(Class<?> clazz){
        return findEditor(null, clazz);
    }
    
    public JournalEditor findEditor(String key, Class<?> clazz){
        JournalEditor editor = findEditor(key, clazz.getName());
        if(editor == null){
            final Class<?>[] interfaces = clazz.getInterfaces();
            for(int i = 0; i < interfaces.length; i++){
                editor = findEditor(key, interfaces[i]);
                if(editor != null){
                    return editor;
                }
            }
        }
        if(editor == null){
            Class<?> tmpClass = clazz;
            while((tmpClass = tmpClass.getSuperclass()) != null){
                editor = findEditor(key, tmpClass);
                if(editor != null){
                    return editor;
                }
            }
        }
        if(editor == null && parentEditorFinder != null){
            editor = parentEditorFinder.findEditor(key, clazz);
        }
        return editor;
    }
    
    private JournalEditor findEditor(String key, String className){
        final Iterator<Map.Entry<String, Pattern>> entries = namePatternMapping.entrySet().iterator();
        while(entries.hasNext()){
            final Map.Entry<String, Pattern> entry = entries.next();
            final String name = entry.getKey();
            final Pattern pattern = entry.getValue();
            final Matcher matcher = pattern.matcher(className);
            if(matcher.matches()){
                final Map<String, JournalEditor> keyEditorMap = editorRegexMapping.get(name);
                JournalEditor editor = keyEditorMap.get(key);
                if(editor == null && key != null){
                    editor = keyEditorMap.get(null);
                }
                return editor;
            }
        }
        return null;
    }
    
    public JournalEditor findEditor(Object obj){
        return findEditor(null, obj);
    }
    
    public JournalEditor findEditor(String key, Object obj){
        JournalEditor editor = null;
        if(parentEditorFinder != null){
            editor = parentEditorFinder.findEditor(key, obj);
        }
        if(editor == null){
            if(obj == null){
                return null;
            }
            return findEditor(key, obj.getClass());
        }
        return editor;
    }
}
