OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 73 | 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
 
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
 
155
    // encapsulate differences about JDOM nodes
156
    static abstract class Node<T> {
157
 
158
        static final Node<Element> elem = new ElementNode();
159
        static final Node<Attribute> attr = new AttributeNode();
160
 
161
        @SuppressWarnings("unchecked")
162
        public static <TT> Node<TT> get(TT o) {
163
            if (o instanceof Attribute)
164
                return (Node<TT>) attr;
165
            else if (o instanceof Element)
166
                return (Node<TT>) elem;
167
            else
168
                throw new IllegalArgumentException("unknown Node: " + o);
169
        }
170
 
171
        @SuppressWarnings("unchecked")
172
        public static <TT> Node<TT> get(Class<TT> clazz) {
173
            if (clazz == Attribute.class)
174
                return (Node<TT>) attr;
175
            else if (clazz == Element.class)
176
                return (Node<TT>) elem;
177
            else
178
                throw new IllegalArgumentException("unknown Node: " + clazz);
179
        }
180
 
181
        public abstract <S> void nextNodes(final List<S> res, final T node, final Step<S> step);
182
 
183
        // viva jdom who doesn't have a common interface for getName() and getNS()
184
        abstract T filter(final T elem, final String name, final String ns);
185
 
186
        @Override
187
        public final String toString() {
188
            return this.getClass().getSimpleName();
189
        }
190
    }
191
 
192
    static class AttributeNode extends Node<Attribute> {
193
 
194
        @Override
195
        public <S> void nextNodes(final List<S> res, final Attribute node, final Step<S> step) {
196
            if (step.getAxis() == Axis.ancestor) {
197
                step.add(node.getParent(), res);
198
            } else
199
                throw new IllegalArgumentException(this + " cannot take the passed step: " + step);
200
        }
201
 
202
        @Override
203
        Attribute filter(Attribute elem, String name, String ns) {
204
            if (elem == null)
205
                return null;
206
            if (name != null && !name.equals(elem.getName()))
207
                return null;
208
            if (ns != null && !ns.equals(elem.getNamespacePrefix()))
209
                return null;
210
            return elem;
211
        }
212
    }
213
 
214
    static class ElementNode extends Node<Element> {
215
 
216
        @SuppressWarnings("unchecked")
217
        @Override
218
        public <S> void nextNodes(final List<S> res, final Element node, final Step<S> step) {
219
            final Axis axis = step.getAxis();
220
            if (axis == Axis.ancestor) {
221
                step.add(node.getParent(), res);
222
            } else if (axis == Axis.attribute) {
20 ilm 223
                final List<?> attributes = node.getAttributes();
17 ilm 224
                final int stop = attributes.size();
225
                for (int i = 0; i < stop; i++) {
226
                    step.add(attributes.get(i), res);
227
                }
228
            } else if (axis == Axis.child) {
229
                // jdom : traversal through the List is best done with a Iterator
230
                for (final Object o : node.getChildren()) {
231
                    step.add(o, res);
232
                }
233
            } else if (axis == Axis.descendantOrSelf) {
234
                step.add(node, res);
235
                final Iterator<S> iter = node.getDescendants(new Filter() {
236
                    @Override
237
                    public boolean matches(Object obj) {
238
                        if (!(obj instanceof Element))
239
                            return false;
240
                        return step.evaluate(obj) != null;
241
                    }
242
                });
243
                while (iter.hasNext()) {
244
                    res.add(iter.next());
245
                }
246
            } else
247
                throw new IllegalArgumentException(this + " cannot take the passed step: " + axis);
248
        }
249
 
250
        @Override
251
        Element filter(Element elem, String name, String ns) {
252
            if (elem == null)
253
                return null;
254
            if (name != null && !name.equals(elem.getName()))
255
                return null;
256
            if (ns != null && !ns.equals(elem.getNamespacePrefix()))
257
                return null;
258
            return elem;
259
        }
260
    }
261
}