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.io.Serializable;
025import java.lang.reflect.Constructor;
026import java.lang.reflect.InvocationTargetException;
027import java.util.ArrayList;
028import java.util.Collections;
029import java.util.Comparator;
030import java.util.HashMap;
031import java.util.List;
032import java.util.Map;
033
034import org.nuxeo.common.xmap.annotation.XObject;
035import org.w3c.dom.Element;
036
037/**
038 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
039 */
040public class XAnnotatedObject {
041
042    final XMap xmap;
043
044    final Class<?> klass;
045
046    final Constructor<?> ctor;
047
048    final Path path;
049
050    final List<XAnnotatedMember> members;
051
052    Sorter sorter;
053
054    public XAnnotatedObject(XMap xmap, Class<?> klass, XObject xob) {
055        try {
056            this.xmap = xmap;
057            this.klass = klass;
058            this.ctor = this.klass.getDeclaredConstructor();
059            ctor.setAccessible(true);
060            path = new Path(xob.value());
061            members = new ArrayList<>();
062            String[] order = xob.order();
063            if (order.length > 0) {
064                sorter = new Sorter(order);
065            }
066        } catch (SecurityException e) {
067            throw new IllegalArgumentException(e);
068        } catch (NoSuchMethodException e) {
069            throw new IllegalArgumentException("Invalid xmap class - no default constructor found", e);
070        }
071    }
072
073    public void addMember(XAnnotatedMember member) {
074        members.add(member);
075    }
076
077    public Path getPath() {
078        return path;
079    }
080
081    public Object newInstance(Context ctx, Element element) {
082        Object ob;
083        try {
084            ob = ctor.newInstance();
085        } catch (InstantiationException e) {
086            throw new IllegalArgumentException(e);
087        } catch (IllegalAccessException e) {
088            throw new IllegalArgumentException(e);
089        } catch (InvocationTargetException e) {
090            if (e.getCause() instanceof RuntimeException) {
091                throw (RuntimeException) e.getCause();
092            }
093            throw new IllegalArgumentException(e);
094        }
095        ctx.push(ob);
096
097        if (sorter != null) {
098            Collections.sort(members, sorter);
099            sorter = null; // sort only once
100        }
101
102        // set annotated members
103        for (XAnnotatedMember member : members) {
104            member.process(ctx, element);
105        }
106
107        return ctx.pop();
108    }
109}
110
111class Sorter implements Comparator<XAnnotatedMember>, Serializable {
112
113    private static final long serialVersionUID = -2546984283687927308L;
114
115    private final Map<String, Integer> order = new HashMap<>();
116
117    Sorter(String[] order) {
118        for (int i = 0; i < order.length; i++) {
119            this.order.put(order[i], i);
120        }
121    }
122
123    @Override
124    public int compare(XAnnotatedMember o1, XAnnotatedMember o2) {
125        String p1 = o1.path == null ? "" : o1.path.path;
126        String p2 = o2.path == null ? "" : o2.path.path;
127        Integer order1 = order.get(p1);
128        Integer order2 = order.get(p2);
129        int n1 = order1 == null ? Integer.MAX_VALUE : order1;
130        int n2 = order2 == null ? Integer.MAX_VALUE : order2;
131        return n1 - n2;
132    }
133
134}