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 */
033public class XAnnotatedMap extends XAnnotatedList {
034
035    protected static final ElementMapVisitor elementMapVisitor = new ElementMapVisitor();
036
037    protected static final ElementValueMapVisitor elementVisitor = new ElementValueMapVisitor();
038
039    protected static final AttributeValueMapVisitor attributeVisitor = new AttributeValueMapVisitor();
040
041    protected final Path key;
042
043    protected final boolean isNullByDefault;
044
045    public XAnnotatedMap(XMap xmap, XAccessor setter, XNodeMap anno) {
046        super(xmap, setter);
047        path = new Path(anno.value());
048        trim = anno.trim();
049        key = new Path(anno.key());
050        type = anno.type();
051        componentType = anno.componentType();
052        valueFactory = xmap.getValueFactory(componentType);
053        xao = xmap.register(componentType);
054        isNullByDefault = anno.nullByDefault();
055    }
056
057    @SuppressWarnings("unchecked")
058    @Override
059    protected Object getValue(Context ctx, Element base) {
060        Map<String, Object> values;
061        try {
062            values = (Map<String, Object>) type.getDeclaredConstructor().newInstance();
063        } catch (ReflectiveOperationException e) {
064            throw new IllegalArgumentException(e);
065        }
066        if (xao != null) {
067            DOMHelper.visitMapNodes(ctx, this, base, path, elementMapVisitor, values);
068        } else {
069            if (path.attribute != null) {
070                // attribute list
071                DOMHelper.visitMapNodes(ctx, this, base, path, attributeVisitor, values);
072            } else {
073                // element list
074                DOMHelper.visitMapNodes(ctx, this, base, path, elementVisitor, values);
075            }
076        }
077        if (isNullByDefault && values.isEmpty()) {
078            values = null;
079        }
080        return values;
081    }
082
083    @Override
084    public void toXML(Object instance, Element parent) {
085        Object v = accessor.getValue(instance);
086        if (v instanceof Map<?, ?>) {
087            @SuppressWarnings("unchecked")
088            Map<String, ?> map = (Map<String, ?>) v;
089            if (xao == null) {
090                for (Map.Entry<String, ?> entry : map.entrySet()) {
091                    String entryKey = entry.getKey();
092                    String value = valueFactory.serialize(null, entry.getValue());
093                    Element e = XMLBuilder.addElement(parent, path);
094                    Element keyElement = XMLBuilder.getOrCreateElement(e, key);
095                    XMLBuilder.fillField(keyElement, entryKey, key.attribute);
096                    XMLBuilder.fillField(e, value, null);
097                }
098            } else {
099                for (Map.Entry<String, ?> entry : map.entrySet()) {
100                    String entryKey = entry.getKey();
101                    Element e = XMLBuilder.addElement(parent, path);
102                    Element keyElement = XMLBuilder.getOrCreateElement(e, key);
103                    XMLBuilder.fillField(keyElement, entryKey, key.attribute);
104                    XMLBuilder.toXML(entry.getValue(), e, xao);
105                }
106            }
107        }
108    }
109}
110
111class ElementMapVisitor implements DOMHelper.NodeMapVisitor {
112
113    @Override
114    public void visitNode(Context ctx, XAnnotatedMember xam, Node node, String key, Map<String, Object> result) {
115        result.put(key, xam.xao.newInstance(ctx, (Element) node));
116    }
117}
118
119class ElementValueMapVisitor implements DOMHelper.NodeMapVisitor {
120    @Override
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    @Override
137    public void visitNode(Context ctx, XAnnotatedMember xam, Node node, String key, Map<String, Object> result) {
138        String val = node.getNodeValue();
139        if (xam.valueFactory != null) {
140            result.put(key, xam.valueFactory.deserialize(ctx, val));
141        } else {
142            // TODO: log warning?
143            result.put(key, val);
144        }
145    }
146}