/*
 * Copyright 2004-2009 the Seasar Foundation and the Others.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package org.seasar.mayaa.impl.engine.processor;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.seasar.mayaa.builder.PathAdjuster;
import org.seasar.mayaa.builder.SequenceIDGenerator;
import org.seasar.mayaa.builder.library.LibraryDefinition;
import org.seasar.mayaa.builder.library.ProcessorDefinition;
import org.seasar.mayaa.builder.library.converter.PropertyConverter;
import org.seasar.mayaa.cycle.script.CompiledScript;
import org.seasar.mayaa.engine.processor.ProcessorProperty;
import org.seasar.mayaa.engine.processor.ProcessorTreeWalker;
import org.seasar.mayaa.engine.specification.NodeAttribute;
import org.seasar.mayaa.engine.specification.PrefixAwareName;
import org.seasar.mayaa.engine.specification.QName;
import org.seasar.mayaa.engine.specification.SpecificationNode;
import org.seasar.mayaa.impl.CONST_IMPL;
import org.seasar.mayaa.impl.cycle.CycleUtil;
import org.seasar.mayaa.impl.cycle.script.LiteralScript;
import org.seasar.mayaa.impl.engine.specification.SpecificationUtil;
import org.seasar.mayaa.impl.provider.ProviderUtil;
import org.seasar.mayaa.impl.util.StringUtil;

/**
 * @author Koji Suga (Gluegent, Inc.)
 */
public class EchoProcessor extends ElementProcessor
        implements CONST_IMPL {

    private static final long serialVersionUID = 3924111635172574833L;

    private PrefixAwareName _customName;
    public void setOriginalNode(SpecificationNode originalNode) {
        super.setOriginalNode(originalNode);
        setupElement(originalNode);
    }

    protected PropertyConverter getConverterForProcessorProperty() {
        ProcessorDefinition processorDef = getProcessorDefinition();
        LibraryDefinition libraryDef = processorDef.getLibraryDefinition();
        PropertyConverter converter =
            libraryDef.getPropertyConverter(ProcessorProperty.class);
        if (converter == null) {
            throw new IllegalStateException();
        }
        return converter;
    }

    /**
     * テンプレート上の属性に相対パス調整が必要な場合、調整して返します。
     * 必要なければそのまま返します。
     *
     * @param adjuster パス調整クラス
     * @param originalNode テンプレート上の対象要素
     * @param attribute テンプレート上の対象属性
     * @return 相対パス調整済み対象属性の値
     */
    protected String getAdjustedValue(PathAdjuster adjuster,
            SpecificationNode originalNode, NodeAttribute attribute) {
        String value = attribute.getValue();
        QName nodeName = originalNode.getQName();
        if (adjuster.isTargetNode(nodeName)) {
            QName attributeName = attribute.getQName();
            if (adjuster.isTargetAttribute(nodeName, attributeName)) {
                String contextPath = CycleUtil.getRequestScope().getContextPath();
                String basePath = contextPath + originalNode.getSystemID();
                return adjuster.adjustRelativePath(basePath, value);
            }
        }
        return value;
    }

    /**
     * 対応するテンプレートの要素の属性をinformal propertyに追加します。
     *
     * @param originalNode 対応するテンプレートの要素
     */
    protected void setupElement(SpecificationNode originalNode) {
        if (_customName != null) {
            super.setName(_customName);
        } else {
            // メモリ消費軽減と速度性能アップのためにキャッシュ利用
            PrefixAwareName prefixAwareName = SpecificationUtil.createPrefixAwareName(
                    originalNode.getQName(), originalNode.getPrefix());
            super.setName(prefixAwareName);
        }
        PropertyConverter converter = getConverterForProcessorProperty();
        PathAdjuster adjuster = ProviderUtil.getPathAdjuster();
        for (Iterator it = originalNode.iterateAttribute(); it.hasNext();) {
            NodeAttribute attribute = (NodeAttribute) it.next();
            String value = getAdjustedValue(adjuster, originalNode, attribute);
            Class expectedClass = getExpectedClass();
            Serializable property = converter.convert(attribute, value, expectedClass);
            PrefixAwareName propName = SpecificationUtil.createPrefixAwareName(
                    attribute.getQName(), attribute.getPrefix());
            super.addInformalProperty(propName, property);
        }
    }

    // MLD property of ElementProcessor
    public void setName(PrefixAwareName name) {
        _customName = name;
    }

    // MLD method of AbstractAttributableProcessor
    public void addInformalProperty(PrefixAwareName name, Object attr) {
        // doNothing
    }

    // ProcessorTreeWalker implements --------------------------------
    public Map getVariables() {
        Iterator it = iterateInformalProperties();
        if (it.hasNext()) {
            Map attributeMap = new HashMap();
            while (it.hasNext()) {
                ProcessorProperty prop = (ProcessorProperty) it.next();
                attributeMap.put(
                        prop.getName().getQName().getLocalName(),
                        resolveEntity(prop.getValue()));
            }
            return attributeMap;
        }
        return null;
    }

    private Object resolveEntity(CompiledScript script) {
        if (script instanceof LiteralScript &&
                String.class.equals(script.getExpectedClass())) {
            return StringUtil.resolveEntity((String) script.execute(null));
        }
        return script.execute(null);
    }

    public ProcessorTreeWalker[] divide(SequenceIDGenerator sequenceIDGenerator) {
        return new ProcessorTreeWalker[] { this };
    }

}
