001/*
002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Nuxeo - initial API and implementation
018 *
019 * $Id$
020 */
021
022package org.nuxeo.common.xmap;
023
024import java.util.Map;
025
026import org.nuxeo.common.xmap.annotation.XNodeMap;
027import org.w3c.dom.Element;
028import org.w3c.dom.Node;
029
030/**
031 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
032 */
033@SuppressWarnings({ "SuppressionAnnotation" })
034public class XAnnotatedMap extends XAnnotatedList {
035
036    protected static final ElementMapVisitor elementMapVisitor = new ElementMapVisitor();
037
038    protected static final ElementValueMapVisitor elementVisitor = new ElementValueMapVisitor();
039
040    protected static final AttributeValueMapVisitor attributeVisitor = new AttributeValueMapVisitor();
041
042    protected final Path key;
043
044    protected final boolean isNullByDefault;
045
046    public XAnnotatedMap(XMap xmap, XAccessor setter, XNodeMap anno) {
047        super(xmap, setter);
048        path = new Path(anno.value());
049        trim = anno.trim();
050        key = new Path(anno.key());
051        type = anno.type();
052        componentType = anno.componentType();
053        valueFactory = xmap.getValueFactory(componentType);
054        xao = xmap.register(componentType);
055        isNullByDefault = anno.nullByDefault();
056    }
057
058    @SuppressWarnings("unchecked")
059    @Override
060    protected Object getValue(Context ctx, Element base) {
061        Map<String, Object> values;
062        try {
063            values = (Map) type.newInstance();
064        } catch (InstantiationException e) {
065            throw new IllegalArgumentException(e);
066        } catch (IllegalAccessException e) {
067            throw new IllegalArgumentException(e);
068        }
069        if (xao != null) {
070            DOMHelper.visitMapNodes(ctx, this, base, path, elementMapVisitor, values);
071        } else {
072            if (path.attribute != null) {
073                // attribute list
074                DOMHelper.visitMapNodes(ctx, this, base, path, attributeVisitor, values);
075            } else {
076                // element list
077                DOMHelper.visitMapNodes(ctx, this, base, path, elementVisitor, values);
078            }
079        }
080        if (isNullByDefault && values.isEmpty()) {
081            values = null;
082        }
083        return values;
084    }
085
086    @Override
087    public void toXML(Object instance, Element parent) {
088        Object v = accessor.getValue(instance);
089        if (v != null && v instanceof Map<?, ?>) {
090            Map<String, ?> map = (Map<String, ?>) v;
091            if (xao == null) {
092                for (Map.Entry<String, ?> entry : map.entrySet()) {
093                    String entryKey = entry.getKey();
094                    String value = valueFactory.serialize(null, entry.getValue());
095                    Element e = XMLBuilder.addElement(parent, path);
096                    Element keyElement = XMLBuilder.getOrCreateElement(e, key);
097                    XMLBuilder.fillField(keyElement, entryKey, key.attribute);
098                    XMLBuilder.fillField(e, value, null);
099                }
100            } else {
101                for (Map.Entry<String, ?> entry : map.entrySet()) {
102                    String entryKey = entry.getKey();
103                    Element e = XMLBuilder.addElement(parent, path);
104                    Element keyElement = XMLBuilder.getOrCreateElement(e, key);
105                    XMLBuilder.fillField(keyElement, entryKey, key.attribute);
106                    XMLBuilder.toXML(entry.getValue(), e, xao);
107                }
108            }
109        }
110    }
111}
112
113class ElementMapVisitor implements DOMHelper.NodeMapVisitor {
114
115    public void visitNode(Context ctx, XAnnotatedMember xam, Node node, String key, Map<String, Object> result) {
116        result.put(key, xam.xao.newInstance(ctx, (Element) node));
117    }
118}
119
120class ElementValueMapVisitor implements DOMHelper.NodeMapVisitor {
121    public void visitNode(Context ctx, XAnnotatedMember xam, Node node, String key, Map<String, Object> result) {
122        String val = node.getTextContent();
123        if (xam.trim) {
124            val = val.trim();
125        }
126        if (xam.valueFactory != null) {
127            result.put(key, xam.valueFactory.deserialize(ctx, val));
128        } else {
129            // TODO: log warning?
130            result.put(key, val);
131        }
132    }
133}
134
135class AttributeValueMapVisitor implements DOMHelper.NodeMapVisitor {
136    public void visitNode(Context ctx, XAnnotatedMember xam, Node node, String key, Map<String, Object> result) {
137        String val = node.getNodeValue();
138        if (xam.valueFactory != null) {
139            result.put(key, xam.valueFactory.deserialize(ctx, val));
140        } else {
141            // TODO: log warning?
142            result.put(key, val);
143        }
144    }
145}