001/*
002 * (C) Copyright 2006-2012 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 */
020
021package org.nuxeo.common.xmap;
022
023import org.nuxeo.common.xmap.annotation.XNode;
024import org.w3c.dom.Element;
025
026/**
027 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
028 */
029public class XAnnotatedMember {
030
031    protected final XAccessor accessor;
032
033    protected Path path;
034
035    protected boolean trim;
036
037    /** The Java type of the described element. */
038    protected Class<?> type;
039
040    /** Not null if the described object is an xannotated object. */
041    protected XAnnotatedObject xao;
042
043    /**
044     * The value factory used to transform strings in objects compatible with this member type. In the case of
045     * collection types this factory is used for collection components.
046     */
047    protected XValueFactory valueFactory;
048
049    protected XAnnotatedMember(XMap xmap, XAccessor accessor) {
050        this.accessor = accessor;
051    }
052
053    public XAnnotatedMember(XMap xmap, XAccessor setter, XNode anno) {
054        accessor = setter;
055        path = new Path(anno.value());
056        trim = anno.trim();
057        type = setter.getType();
058        valueFactory = xmap.getValueFactory(type);
059        if (valueFactory == null && type.isEnum()) {
060            valueFactory = new XValueFactory() {
061                @Override
062                public String serialize(Context arg0, Object arg1) {
063                    return ((Enum<?>) arg1).name();
064                }
065
066                @SuppressWarnings("unchecked")
067                @Override
068                public Object deserialize(Context arg0, String arg1) {
069                    @SuppressWarnings("rawtypes")
070                    Class<Enum> enumType = (Class<Enum>) type;
071                    return Enum.valueOf(enumType, arg1);
072                }
073            };
074            xmap.setValueFactory(type, valueFactory);
075        }
076        xao = xmap.register(type);
077    }
078
079    protected void setValue(Object instance, Object value) {
080        try {
081            accessor.setValue(instance, value);
082        } catch (IllegalArgumentException e) {
083            throw new IllegalArgumentException(
084                    String.format("%s, setter=%s, value=%s", e.getMessage(), accessor, value), e);
085        }
086    }
087
088    public void toXML(Object instance, Element parent) {
089        Element e = XMLBuilder.getOrCreateElement(parent, path);
090        Object v = accessor.getValue(instance);
091        if (xao == null) {
092            if (v != null && valueFactory != null) {
093                String value = valueFactory.serialize(null, v);
094                if (value != null) {
095
096                    XMLBuilder.fillField(e, value, path.attribute);
097                }
098            }
099        } else {
100            XMLBuilder.toXML(v, e, xao);
101        }
102    }
103
104    public void process(Context ctx, Element element) {
105        Object value = getValue(ctx, element);
106        if (value != null) {
107            setValue(ctx.getObject(), value);
108        }
109    }
110
111    protected Object getValue(Context ctx, Element base) {
112        if (xao != null) {
113            Element el = (Element) DOMHelper.getElementNode(base, path);
114            if (el == null) {
115                return null;
116            } else {
117                return xao.newInstance(ctx, el);
118            }
119        }
120        // scalar field
121        if (type == Element.class) {
122            // allow DOM elements as values
123            return base;
124        }
125        String val = DOMHelper.getNodeValue(base, path);
126        if (val != null) {
127            if (trim) {
128                val = val.trim();
129            }
130            if (valueFactory == null) {
131                throw new NullPointerException("Missing XValueFactory for " + type);
132            }
133            return valueFactory.deserialize(ctx, val);
134        }
135        return null;
136    }
137
138}