OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 83 | 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
 
83 ilm 40
    private static final SimpleXMLPath<Attribute> ALL_ATTRIBUTES = allAttributes(null, null);
41
    private static final SimpleXMLPath<Element> ALL_ELEMENTS = allElements(null, null);
42
 
17 ilm 43
    public static <T> SimpleXMLPath<T> create(final List<Step<?>> steps, final Step<T> lastStep) {
73 ilm 44
        return new SimpleXMLPath<T>(Collections.unmodifiableList(new ArrayList<Step<?>>(steps)), lastStep);
17 ilm 45
    }
46
 
47
    public static <T> SimpleXMLPath<T> create(final Step<T> lastStep) {
73 ilm 48
        return new SimpleXMLPath<T>(Collections.<Step<?>> emptyList(), lastStep);
17 ilm 49
    }
50
 
51
    public static <T> SimpleXMLPath<T> create(final Step<?> first, final Step<T> lastStep) {
73 ilm 52
        return new SimpleXMLPath<T>(Collections.<Step<?>> singletonList(first), lastStep);
17 ilm 53
    }
54
 
55
    public static <T> SimpleXMLPath<T> create(final Step<?> first, final Step<?> second, final Step<T> lastStep) {
73 ilm 56
        return new SimpleXMLPath<T>(Arrays.<Step<?>> asList(first, second), lastStep);
17 ilm 57
    }
58
 
20 ilm 59
    /**
83 ilm 60
     * Create a path searching for all descendant attributes. The returned instance is immutable.
61
     *
62
     * @return a path searching attributes in all {@link Axis#descendantOrSelf} elements.
63
     */
64
    public static SimpleXMLPath<Attribute> allAttributes() {
65
        return ALL_ATTRIBUTES;
66
    }
67
 
68
    /**
20 ilm 69
     * Create a path searching for all descendant attributes with the passed name and namespace.
83 ilm 70
     * I.e. in XPath this would be ".//@ns:name". The returned instance is immutable.
20 ilm 71
     *
72
     * @param name the name of attributes.
73
     * @param ns the namespace of attributes.
74
     * @return a path searching attributes in all {@link Axis#descendantOrSelf} elements.
75
     */
76
    public static SimpleXMLPath<Attribute> allAttributes(final String name, final String ns) {
77
        return create(Step.createElementStep(Axis.descendantOrSelf, null), Step.createAttributeStep(name, ns));
78
    }
79
 
80
    /**
83 ilm 81
     * Create a path searching for all descendant elements. The returned instance is immutable.
82
     *
83
     * @return a path searching all {@link Axis#descendantOrSelf} elements.
84
     */
85
    public static SimpleXMLPath<Element> allElements() {
86
        return ALL_ELEMENTS;
87
    }
88
 
89
    /**
20 ilm 90
     * Create a path searching for all descendant elements with the passed name and namespace. I.e.
83 ilm 91
     * in XPath this would be ".//ns:name". The returned instance is immutable.
20 ilm 92
     *
93
     * @param name the name of elements.
94
     * @param ns the namespace of elements.
95
     * @return a path searching all {@link Axis#descendantOrSelf} elements.
96
     */
97
    public static SimpleXMLPath<Element> allElements(final String name, final String ns) {
98
        return create(Step.createElementStep(Axis.descendantOrSelf, name, ns));
99
    }
100
 
17 ilm 101
    private final List<Step<?>> items;
102
    private final Step<T> lastItem;
103
 
73 ilm 104
    // private since we don't copy steps
105
    private SimpleXMLPath(final List<Step<?>> steps, Step<T> lastStep) {
17 ilm 106
        this.lastItem = lastStep;
73 ilm 107
        this.items = steps;
17 ilm 108
    }
109
 
110
    // return Element or Attribute
111
    public final T selectSingleNode(final Object n) {
112
        return selectSingleNode(n, this.items);
113
    }
114
 
115
    private final T selectSingleNode(final Object currentNode, List<Step<?>> steps) {
116
        final int size = steps.size();
117
        if (size > 0) {
118
            final Step<?> currentStep = steps.get(0);
119
 
120
            final List<?> nextNodes = currentStep.nextNodes(Node.get(currentNode), currentNode);
121
            // MAYBE add an index argument instead of creating a sublist
122
            final List<Step<?>> nextSteps = steps.subList(1, size);
123
            final int stop = nextNodes.size();
124
            for (int i = 0; i < stop; i++) {
125
                final T finalNode = this.selectSingleNode(nextNodes.get(i), nextSteps);
126
                if (finalNode != null)
127
                    return finalNode;
128
            }
129
            return null;
130
        } else {
131
            return CollectionUtils.getFirst(this.lastItem.nextNodes(Node.get(currentNode), currentNode));
132
        }
133
    }
134
 
135
    public final List<T> selectNodes(final Object n) {
73 ilm 136
        return this.selectNodes(Collections.singletonList(n));
137
    }
138
 
139
    public final List<T> selectNodes(final List<?> nodes) {
140
        List<?> currentNodes = nodes;
17 ilm 141
        final int stop = this.items.size();
142
        for (int i = 0; i < stop; i++) {
143
            final Step<?> currentStep = this.items.get(i);
144
 
145
            final List<?> nextNodes = currentStep.nextNodes(currentNodes);
146
            if (nextNodes.isEmpty())
147
                return Collections.emptyList();
148
            else
149
                currentNodes = nextNodes;
150
 
151
        }
152
        return this.lastItem.nextNodes(currentNodes);
153
    }
154
 
180 ilm 155
    public final List<String> selectValues(final Object n) {
156
        return this.selectValues(Collections.singletonList(n));
157
    }
158
 
159
    /**
160
     * Return the string-value properties of the selected nodes.
161
     *
162
     * @param nodes the context.
163
     * @return the string values.
164
     * @see <a href="https://www.w3.org/TR/xpath-datamodel/#dm-string-value">string-value</a>
165
     */
166
    public final List<String> selectValues(final List<?> nodes) {
167
        final List<T> lastNodes = this.selectNodes(nodes);
168
        return this.lastItem.getValues(lastNodes);
169
    }
170
 
17 ilm 171
    // encapsulate differences about JDOM nodes
172
    static abstract class Node<T> {
173
 
174
        static final Node<Element> elem = new ElementNode();
175
        static final Node<Attribute> attr = new AttributeNode();
176
 
177
        @SuppressWarnings("unchecked")
178
        public static <TT> Node<TT> get(TT o) {
179
            if (o instanceof Attribute)
180
                return (Node<TT>) attr;
181
            else if (o instanceof Element)
182
                return (Node<TT>) elem;
183
            else
184
                throw new IllegalArgumentException("unknown Node: " + o);
185
        }
186
 
187
        @SuppressWarnings("unchecked")
188
        public static <TT> Node<TT> get(Class<TT> clazz) {
189
            if (clazz == Attribute.class)
190
                return (Node<TT>) attr;
191
            else if (clazz == Element.class)
192
                return (Node<TT>) elem;
193
            else
194
                throw new IllegalArgumentException("unknown Node: " + clazz);
195
        }
196
 
197
        public abstract <S> void nextNodes(final List<S> res, final T node, final Step<S> step);
198
 
199
        // viva jdom who doesn't have a common interface for getName() and getNS()
200
        abstract T filter(final T elem, final String name, final String ns);
201
 
180 ilm 202
        // Returns the XPath 1.0 string value of the passed node
203
        protected abstract String getValue(T n);
204
 
17 ilm 205
        @Override
206
        public final String toString() {
207
            return this.getClass().getSimpleName();
208
        }
209
    }
210
 
211
    static class AttributeNode extends Node<Attribute> {
212
 
213
        @Override
214
        public <S> void nextNodes(final List<S> res, final Attribute node, final Step<S> step) {
215
            if (step.getAxis() == Axis.ancestor) {
216
                step.add(node.getParent(), res);
217
            } else
218
                throw new IllegalArgumentException(this + " cannot take the passed step: " + step);
219
        }
220
 
221
        @Override
222
        Attribute filter(Attribute elem, String name, String ns) {
223
            if (elem == null)
224
                return null;
225
            if (name != null && !name.equals(elem.getName()))
226
                return null;
227
            if (ns != null && !ns.equals(elem.getNamespacePrefix()))
228
                return null;
229
            return elem;
230
        }
180 ilm 231
 
232
        @Override
233
        protected String getValue(Attribute n) {
234
            return n.getValue();
235
        }
17 ilm 236
    }
237
 
238
    static class ElementNode extends Node<Element> {
239
 
240
        @SuppressWarnings("unchecked")
241
        @Override
242
        public <S> void nextNodes(final List<S> res, final Element node, final Step<S> step) {
243
            final Axis axis = step.getAxis();
244
            if (axis == Axis.ancestor) {
245
                step.add(node.getParent(), res);
246
            } else if (axis == Axis.attribute) {
20 ilm 247
                final List<?> attributes = node.getAttributes();
17 ilm 248
                final int stop = attributes.size();
249
                for (int i = 0; i < stop; i++) {
250
                    step.add(attributes.get(i), res);
251
                }
252
            } else if (axis == Axis.child) {
253
                // jdom : traversal through the List is best done with a Iterator
254
                for (final Object o : node.getChildren()) {
255
                    step.add(o, res);
256
                }
257
            } else if (axis == Axis.descendantOrSelf) {
258
                step.add(node, res);
259
                final Iterator<S> iter = node.getDescendants(new Filter() {
260
                    @Override
261
                    public boolean matches(Object obj) {
262
                        if (!(obj instanceof Element))
263
                            return false;
264
                        return step.evaluate(obj) != null;
265
                    }
266
                });
267
                while (iter.hasNext()) {
268
                    res.add(iter.next());
269
                }
270
            } else
271
                throw new IllegalArgumentException(this + " cannot take the passed step: " + axis);
272
        }
273
 
274
        @Override
275
        Element filter(Element elem, String name, String ns) {
276
            if (elem == null)
277
                return null;
278
            if (name != null && !name.equals(elem.getName()))
279
                return null;
280
            if (ns != null && !ns.equals(elem.getNamespacePrefix()))
281
                return null;
282
            return elem;
283
        }
180 ilm 284
 
285
        @Override
286
        protected String getValue(Element n) {
287
            return n.getValue();
288
        }
17 ilm 289
    }
290
}