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.lang.reflect.Array;
025import java.util.ArrayList;
026import java.util.Collection;
027import java.util.List;
028
029import org.nuxeo.common.collections.PrimitiveArrays;
030import org.nuxeo.common.xmap.annotation.XNodeList;
031import org.w3c.dom.Element;
032import org.w3c.dom.Node;
033
034/**
035 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
036 */
037@SuppressWarnings({ "SuppressionAnnotation" })
038public class XAnnotatedList extends XAnnotatedMember {
039
040    protected static final ElementVisitor elementListVisitor = new ElementVisitor();
041
042    protected static final ElementValueVisitor elementVisitor = new ElementValueVisitor();
043
044    protected static final AttributeValueVisitor attributeVisitor = new AttributeValueVisitor();
045
046    // indicates the type of the collection components
047    protected Class componentType;
048
049    protected boolean isNullByDefault;
050
051    protected XAnnotatedList(XMap xmap, XAccessor setter) {
052        super(xmap, setter);
053    }
054
055    public XAnnotatedList(XMap xmap, XAccessor setter, XNodeList anno) {
056        super(xmap, setter);
057        path = new Path(anno.value());
058        trim = anno.trim();
059        type = anno.type();
060        componentType = anno.componentType();
061        valueFactory = xmap.getValueFactory(componentType);
062        xao = xmap.register(componentType);
063        isNullByDefault = anno.nullByDefault();
064    }
065
066    @SuppressWarnings("unchecked")
067    @Override
068    protected Object getValue(Context ctx, Element base) {
069        List<Object> values = new ArrayList<Object>();
070        if (xao != null) {
071            DOMHelper.visitNodes(ctx, this, base, path, elementListVisitor, values);
072        } else {
073            if (path.attribute != null) {
074                // attribute list
075                DOMHelper.visitNodes(ctx, this, base, path, attributeVisitor, values);
076            } else {
077                // element list
078                DOMHelper.visitNodes(ctx, this, base, path, elementVisitor, values);
079            }
080        }
081
082        if (isNullByDefault && values.isEmpty()) {
083            return null;
084        }
085
086        if (type != ArrayList.class) {
087            if (type.isArray()) {
088                if (componentType.isPrimitive()) {
089                    // primitive arrays cannot be casted to Object[]
090                    return PrimitiveArrays.toPrimitiveArray(values, componentType);
091                } else {
092                    return values.toArray((Object[]) Array.newInstance(componentType, values.size()));
093                }
094            } else {
095                try {
096                    Collection col = (Collection) type.newInstance();
097                    col.addAll(values);
098                    return col;
099                } catch (InstantiationException e) {
100                    throw new IllegalArgumentException(e);
101                } catch (IllegalAccessException e) {
102                    throw new IllegalArgumentException(e);
103                }
104            }
105        }
106
107        return values;
108    }
109
110    @Override
111    public void toXML(Object instance, Element parent) {
112        Object v = accessor.getValue(instance);
113        if (v != null) {
114            Object[] objects = null;
115            if (v instanceof Object[]) {
116                objects = (Object[]) v;
117            } else if (v instanceof List) {
118                objects = ((List) v).toArray();
119            } else if (v instanceof Collection) {
120                objects = ((Collection) v).toArray();
121            } else {
122                objects = PrimitiveArrays.toObjectArray(v);
123            }
124            if (objects != null) {
125                if (xao == null) {
126                    for (Object o : objects) {
127                        String value = valueFactory.serialize(null, o);
128                        if (value != null) {
129                            Element e = XMLBuilder.addElement(parent, path);
130                            XMLBuilder.fillField(e, value, path.attribute);
131                        }
132                    }
133                } else {
134                    for (Object o : objects) {
135                        Element e = XMLBuilder.addElement(parent, path);
136                        XMLBuilder.toXML(o, e, xao);
137                    }
138                }
139            }
140        }
141    }
142}
143
144class ElementVisitor implements DOMHelper.NodeVisitor {
145
146    @Override
147    public void visitNode(Context ctx, XAnnotatedMember xam, Node node, Collection<Object> result) {
148        result.add(xam.xao.newInstance(ctx, (Element) node));
149    }
150}
151
152class ElementValueVisitor implements DOMHelper.NodeVisitor {
153    @Override
154    public void visitNode(Context ctx, XAnnotatedMember xam, Node node, Collection<Object> result) {
155        String val = node.getTextContent();
156        if (xam.trim) {
157            val = val.trim();
158        }
159        if (xam.valueFactory != null) {
160            result.add(xam.valueFactory.deserialize(ctx, val));
161        } else {
162            // TODO: log warning?
163            result.add(val);
164        }
165    }
166}
167
168class AttributeValueVisitor implements DOMHelper.NodeVisitor {
169    @Override
170    public void visitNode(Context ctx, XAnnotatedMember xam, Node node, Collection<Object> result) {
171        String val = node.getNodeValue();
172        if (xam.valueFactory != null) {
173            result.add(xam.valueFactory.deserialize(ctx, val));
174        } else {
175            // TODO: log warning?
176            result.add(val);
177        }
178    }
179}