/*
 * Copyright 2009 Inspire-Software.com
 *
 *    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.yes.cart.domain.entity.xml.converter;

import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.converters.collections.AbstractCollectionConverter;
import com.thoughtworks.xstream.io.ExtendedHierarchicalStreamWriterHelper;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.mapper.Mapper;

import java.util.Map;

/**
 * User: denispavlov
 * Date: 12-08-05
 * Time: 10:56 PM
 */
public class MapConverter extends AbstractCollectionConverter {

    private final String entryNode;
    private final String entryNodeKey;
    private final String entryNodeValue;

    public MapConverter(Mapper mapper, final String entryNode, final String entryNodeKey, final String entryNodeValue) {
        super(mapper);
        this.entryNode = entryNode;
        this.entryNodeKey = entryNodeKey;
        this.entryNodeValue = entryNodeValue;
    }

    @Override
    public boolean canConvert(Class type) {
        return type.isAssignableFrom(Map.class);
    }

    @Override
    public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
        Map map = (Map) source;
        for (final Object o : map.entrySet()) {
            Map.Entry entry = (Map.Entry) o;
            writer.startNode(this.entryNode);

            writeItem(this.entryNodeKey, entry.getKey(), context, writer);
            writeItem(this.entryNodeValue, entry.getValue(), context, writer);

            writer.endNode();
        }
    }

    protected void writeItem(String nodeName, Object item, MarshallingContext context, HierarchicalStreamWriter writer) {
        // PUBLISHED API METHOD! If changing signature, ensure backwards compatibility.
        if (item != null) {
            ExtendedHierarchicalStreamWriterHelper.startNode(writer, nodeName, item.getClass());
            context.convertAnother(item);
            writer.endNode();
        }
    }

    @Override
    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
        Map map = (Map) createCollection(context.getRequiredType());
        populateMap(reader, context, map);
        return map;
    }

    protected void populateMap(HierarchicalStreamReader reader, UnmarshallingContext context, Map map) {
        while (reader.hasMoreChildren()) {
            reader.moveDown();

            reader.moveDown();
            Object key = readBareItem(reader, context, map);
            reader.moveUp();

            reader.moveDown();
            Object value = readBareItem(reader, context, map);
            reader.moveUp();

            map.put(key, value);

            reader.moveUp();
        }
    }
}
