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}