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