OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 20 | Rev 83 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
17 ilm 1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
9
 * language governing permissions and limitations under the License.
10
 *
11
 * When distributing the software, include this License Header Notice in each file.
12
 */
13
 
14
 package org.openconcerto.xml;
15
 
16
import org.openconcerto.utils.CollectionUtils;
17
import org.openconcerto.xml.Step.Axis;
18
 
19
import java.util.ArrayList;
20
import java.util.Arrays;
21
import java.util.Collections;
22
import java.util.Iterator;
23
import java.util.List;
24
 
25
import org.jdom.Attribute;
26
import org.jdom.Element;
27
import org.jdom.filter.Filter;
28
import org.jdom.xpath.XPath;
29
 
30
/**
73 ilm 31
 * Like an {@link XPath} with less features but a greater speed. Thread-safe if its {@link Step
32
 * steps} are.
17 ilm 33
 *
34
 * @author Sylvain CUAZ
35
 *
36
 * @param <T> type of result.
37
 */
38
public final class SimpleXMLPath<T> {
39
 
40
    public static <T> SimpleXMLPath<T> create(final List<Step<?>> steps, final Step<T> lastStep) {
73 ilm 41
        return new SimpleXMLPath<T>(Collections.unmodifiableList(new ArrayList<Step<?>>(steps)), lastStep);
17 ilm 42
    }
43
 
44
    public static <T> SimpleXMLPath<T> create(final Step<T> lastStep) {
73 ilm 45
        return new SimpleXMLPath<T>(Collections.<Step<?>> emptyList(), lastStep);
17 ilm 46
    }
47
 
48
    public static <T> SimpleXMLPath<T> create(final Step<?> first, final Step<T> lastStep) {
73 ilm 49
        return new SimpleXMLPath<T>(Collections.<Step<?>> singletonList(first), lastStep);
17 ilm 50
    }
51
 
52
    public static <T> SimpleXMLPath<T> create(final Step<?> first, final Step<?> second, final Step<T> lastStep) {
73 ilm 53
        return new SimpleXMLPath<T>(Arrays.<Step<?>> asList(first, second), lastStep);
17 ilm 54
    }
55
 
20 ilm 56
    /**
57
     * Create a path searching for all descendant attributes with the passed name and namespace.
58
     * I.e. in XPath this would be ".//@ns:name".
59
     *
60
     * @param name the name of attributes.
61
     * @param ns the namespace of attributes.
62
     * @return a path searching attributes in all {@link Axis#descendantOrSelf} elements.
63
     */
64
    public static SimpleXMLPath<Attribute> allAttributes(final String name, final String ns) {
65
        return create(Step.createElementStep(Axis.descendantOrSelf, null), Step.createAttributeStep(name, ns));
66
    }
67
 
68
    /**
69
     * Create a path searching for all descendant elements with the passed name and namespace. I.e.
70
     * in XPath this would be ".//ns:name".
71
     *
72
     * @param name the name of elements.
73
     * @param ns the namespace of elements.
74
     * @return a path searching all {@link Axis#descendantOrSelf} elements.
75
     */
76
    public static SimpleXMLPath<Element> allElements(final String name, final String ns) {
77
        return create(Step.createElementStep(Axis.descendantOrSelf, name, ns));
78
    }
79
 
17 ilm 80
    private final List<Step<?>> items;
81
    private final Step<T> lastItem;
82
 
73 ilm 83
    // private since we don't copy steps
84
    private SimpleXMLPath(final List<Step<?>> steps, Step<T> lastStep) {
17 ilm 85
        this.lastItem = lastStep;
73 ilm 86
        this.items = steps;
17 ilm 87
    }
88
 
89
    // return Element or Attribute
90
    public final T selectSingleNode(final Object n) {
91
        return selectSingleNode(n, this.items);
92
    }
93
 
94
    private final T selectSingleNode(final Object currentNode, List<Step<?>> steps) {
95
        final int size = steps.size();
96
        if (size > 0) {
97
            final Step<?> currentStep = steps.get(0);
98
 
99
            final List<?> nextNodes = currentStep.nextNodes(Node.get(currentNode), currentNode);
100
            // MAYBE add an index argument instead of creating a sublist
101
            final List<Step<?>> nextSteps = steps.subList(1, size);
102
            final int stop = nextNodes.size();
103
            for (int i = 0; i < stop; i++) {
104
                final T finalNode = this.selectSingleNode(nextNodes.get(i), nextSteps);
105
                if (finalNode != null)
106
                    return finalNode;
107
            }
108
            return null;
109
        } else {
110
            return CollectionUtils.getFirst(this.lastItem.nextNodes(Node.get(currentNode), currentNode));
111
        }
112
    }
113
 
114
    public final List<T> selectNodes(final Object n) {
73 ilm 115
        return this.selectNodes(Collections.singletonList(n));
116
    }
117
 
118
    public final List<T> selectNodes(final List<?> nodes) {
119
        List<?> currentNodes = nodes;
17 ilm 120
        final int stop = this.items.size();
121
        for (int i = 0; i < stop; i++) {
122
            final Step<?> currentStep = this.items.get(i);
123
 
124
            final List<?> nextNodes = currentStep.nextNodes(currentNodes);
125
            if (nextNodes.isEmpty())
126
                return Collections.emptyList();
127
            else
128
                currentNodes = nextNodes;
129
 
130
        }
131
        return this.lastItem.nextNodes(currentNodes);
132
    }
133
 
134
    // encapsulate differences about JDOM nodes
135
    static abstract class Node<T> {
136
 
137
        static final Node<Element> elem = new ElementNode();
138
        static final Node<Attribute> attr = new AttributeNode();
139
 
140
        @SuppressWarnings("unchecked")
141
        public static <TT> Node<TT> get(TT o) {
142
            if (o instanceof Attribute)
143
                return (Node<TT>) attr;
144
            else if (o instanceof Element)
145
                return (Node<TT>) elem;
146
            else
147
                throw new IllegalArgumentException("unknown Node: " + o);
148
        }
149
 
150
        @SuppressWarnings("unchecked")
151
        public static <TT> Node<TT> get(Class<TT> clazz) {
152
            if (clazz == Attribute.class)
153
                return (Node<TT>) attr;
154
            else if (clazz == Element.class)
155
                return (Node<TT>) elem;
156
            else
157
                throw new IllegalArgumentException("unknown Node: " + clazz);
158
        }
159
 
160
        public abstract <S> void nextNodes(final List<S> res, final T node, final Step<S> step);
161
 
162
        // viva jdom who doesn't have a common interface for getName() and getNS()
163
        abstract T filter(final T elem, final String name, final String ns);
164
 
165
        @Override
166
        public final String toString() {
167
            return this.getClass().getSimpleName();
168
        }
169
    }
170
 
171
    static class AttributeNode extends Node<Attribute> {
172
 
173
        @Override
174
        public <S> void nextNodes(final List<S> res, final Attribute node, final Step<S> step) {
175
            if (step.getAxis() == Axis.ancestor) {
176
                step.add(node.getParent(), res);
177
            } else
178
                throw new IllegalArgumentException(this + " cannot take the passed step: " + step);
179
        }
180
 
181
        @Override
182
        Attribute filter(Attribute elem, String name, String ns) {
183
            if (elem == null)
184
                return null;
185
            if (name != null && !name.equals(elem.getName()))
186
                return null;
187
            if (ns != null && !ns.equals(elem.getNamespacePrefix()))
188
                return null;
189
            return elem;
190
        }
191
    }
192
 
193
    static class ElementNode extends Node<Element> {
194
 
195
        @SuppressWarnings("unchecked")
196
        @Override
197
        public <S> void nextNodes(final List<S> res, final Element node, final Step<S> step) {
198
            final Axis axis = step.getAxis();
199
            if (axis == Axis.ancestor) {
200
                step.add(node.getParent(), res);
201
            } else if (axis == Axis.attribute) {
20 ilm 202
                final List<?> attributes = node.getAttributes();
17 ilm 203
                final int stop = attributes.size();
204
                for (int i = 0; i < stop; i++) {
205
                    step.add(attributes.get(i), res);
206
                }
207
            } else if (axis == Axis.child) {
208
                // jdom : traversal through the List is best done with a Iterator
209
                for (final Object o : node.getChildren()) {
210
                    step.add(o, res);
211
                }
212
            } else if (axis == Axis.descendantOrSelf) {
213
                step.add(node, res);
214
                final Iterator<S> iter = node.getDescendants(new Filter() {
215
                    @Override
216
                    public boolean matches(Object obj) {
217
                        if (!(obj instanceof Element))
218
                            return false;
219
                        return step.evaluate(obj) != null;
220
                    }
221
                });
222
                while (iter.hasNext()) {
223
                    res.add(iter.next());
224
                }
225
            } else
226
                throw new IllegalArgumentException(this + " cannot take the passed step: " + axis);
227
        }
228
 
229
        @Override
230
        Element filter(Element elem, String name, String ns) {
231
            if (elem == null)
232
                return null;
233
            if (name != null && !name.equals(elem.getName()))
234
                return null;
235
            if (ns != null && !ns.equals(elem.getNamespacePrefix()))
236
                return null;
237
            return elem;
238
        }
239
    }
240
}