OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 20 | Go to most recent revision | Details | 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
/**
31
 * Like an {@link XPath} with less features but a greater speed.
32
 *
33
 * @author Sylvain CUAZ
34
 *
35
 * @param <T> type of result.
36
 */
37
public final class SimpleXMLPath<T> {
38
 
39
    public static <T> SimpleXMLPath<T> create(final List<Step<?>> steps, final Step<T> lastStep) {
40
        final SimpleXMLPath<T> res = new SimpleXMLPath<T>(lastStep);
41
        for (final Step<?> s : steps)
42
            res.add(s);
43
        return res;
44
    }
45
 
46
    public static <T> SimpleXMLPath<T> create(final Step<T> lastStep) {
47
        return new SimpleXMLPath<T>(lastStep);
48
    }
49
 
50
    public static <T> SimpleXMLPath<T> create(final Step<?> first, final Step<T> lastStep) {
51
        final SimpleXMLPath<T> res = new SimpleXMLPath<T>(lastStep);
52
        res.add(first);
53
        return res;
54
    }
55
 
56
    public static <T> SimpleXMLPath<T> create(final Step<?> first, final Step<?> second, final Step<T> lastStep) {
57
        return create(Arrays.<Step<?>> asList(first, second), lastStep);
58
    }
59
 
60
    private final List<Step<?>> items;
61
    private final Step<T> lastItem;
62
 
63
    private SimpleXMLPath(Step<T> lastStep) {
64
        this.lastItem = lastStep;
65
        this.items = new ArrayList<Step<?>>();
66
    }
67
 
68
    public final SimpleXMLPath add(final String name) {
69
        return this.add(Step.createElementStep(name, null, null));
70
    }
71
 
72
    public final SimpleXMLPath add(Step<?> step) {
73
        this.items.add(step);
74
        return this;
75
    }
76
 
77
    // return Element or Attribute
78
    public final T selectSingleNode(final Object n) {
79
        return selectSingleNode(n, this.items);
80
    }
81
 
82
    private final T selectSingleNode(final Object currentNode, List<Step<?>> steps) {
83
        final int size = steps.size();
84
        if (size > 0) {
85
            final Step<?> currentStep = steps.get(0);
86
 
87
            final List<?> nextNodes = currentStep.nextNodes(Node.get(currentNode), currentNode);
88
            // MAYBE add an index argument instead of creating a sublist
89
            final List<Step<?>> nextSteps = steps.subList(1, size);
90
            final int stop = nextNodes.size();
91
            for (int i = 0; i < stop; i++) {
92
                final T finalNode = this.selectSingleNode(nextNodes.get(i), nextSteps);
93
                if (finalNode != null)
94
                    return finalNode;
95
            }
96
            return null;
97
        } else {
98
            return CollectionUtils.getFirst(this.lastItem.nextNodes(Node.get(currentNode), currentNode));
99
        }
100
    }
101
 
102
    public final List<T> selectNodes(final Object n) {
103
        List<?> currentNodes = Collections.singletonList(n);
104
        final int stop = this.items.size();
105
        for (int i = 0; i < stop; i++) {
106
            final Step<?> currentStep = this.items.get(i);
107
 
108
            final List<?> nextNodes = currentStep.nextNodes(currentNodes);
109
            if (nextNodes.isEmpty())
110
                return Collections.emptyList();
111
            else
112
                currentNodes = nextNodes;
113
 
114
        }
115
        return this.lastItem.nextNodes(currentNodes);
116
    }
117
 
118
    // encapsulate differences about JDOM nodes
119
    static abstract class Node<T> {
120
 
121
        static final Node<Element> elem = new ElementNode();
122
        static final Node<Attribute> attr = new AttributeNode();
123
 
124
        @SuppressWarnings("unchecked")
125
        public static <TT> Node<TT> get(TT o) {
126
            if (o instanceof Attribute)
127
                return (Node<TT>) attr;
128
            else if (o instanceof Element)
129
                return (Node<TT>) elem;
130
            else
131
                throw new IllegalArgumentException("unknown Node: " + o);
132
        }
133
 
134
        @SuppressWarnings("unchecked")
135
        public static <TT> Node<TT> get(Class<TT> clazz) {
136
            if (clazz == Attribute.class)
137
                return (Node<TT>) attr;
138
            else if (clazz == Element.class)
139
                return (Node<TT>) elem;
140
            else
141
                throw new IllegalArgumentException("unknown Node: " + clazz);
142
        }
143
 
144
        public abstract <S> void nextNodes(final List<S> res, final T node, final Step<S> step);
145
 
146
        // viva jdom who doesn't have a common interface for getName() and getNS()
147
        abstract T filter(final T elem, final String name, final String ns);
148
 
149
        @Override
150
        public final String toString() {
151
            return this.getClass().getSimpleName();
152
        }
153
    }
154
 
155
    static class AttributeNode extends Node<Attribute> {
156
 
157
        @Override
158
        public <S> void nextNodes(final List<S> res, final Attribute node, final Step<S> step) {
159
            if (step.getAxis() == Axis.ancestor) {
160
                step.add(node.getParent(), res);
161
            } else
162
                throw new IllegalArgumentException(this + " cannot take the passed step: " + step);
163
        }
164
 
165
        @Override
166
        Attribute filter(Attribute elem, String name, String ns) {
167
            if (elem == null)
168
                return null;
169
            if (name != null && !name.equals(elem.getName()))
170
                return null;
171
            if (ns != null && !ns.equals(elem.getNamespacePrefix()))
172
                return null;
173
            return elem;
174
        }
175
    }
176
 
177
    static class ElementNode extends Node<Element> {
178
 
179
        @SuppressWarnings("unchecked")
180
        @Override
181
        public <S> void nextNodes(final List<S> res, final Element node, final Step<S> step) {
182
            final Axis axis = step.getAxis();
183
            if (axis == Axis.ancestor) {
184
                step.add(node.getParent(), res);
185
            } else if (axis == Axis.attribute) {
186
                final List attributes = node.getAttributes();
187
                final int stop = attributes.size();
188
                for (int i = 0; i < stop; i++) {
189
                    step.add(attributes.get(i), res);
190
                }
191
            } else if (axis == Axis.child) {
192
                // jdom : traversal through the List is best done with a Iterator
193
                for (final Object o : node.getChildren()) {
194
                    step.add(o, res);
195
                }
196
            } else if (axis == Axis.descendantOrSelf) {
197
                step.add(node, res);
198
                final Iterator<S> iter = node.getDescendants(new Filter() {
199
                    @Override
200
                    public boolean matches(Object obj) {
201
                        if (!(obj instanceof Element))
202
                            return false;
203
                        return step.evaluate(obj) != null;
204
                    }
205
                });
206
                while (iter.hasNext()) {
207
                    res.add(iter.next());
208
                }
209
            } else
210
                throw new IllegalArgumentException(this + " cannot take the passed step: " + axis);
211
        }
212
 
213
        @Override
214
        Element filter(Element elem, String name, String ns) {
215
            if (elem == null)
216
                return null;
217
            if (name != null && !name.equals(elem.getName()))
218
                return null;
219
            if (ns != null && !ns.equals(elem.getNamespacePrefix()))
220
                return null;
221
            return elem;
222
        }
223
    }
224
}