~ubuntu-branches/ubuntu/saucy/commons-configuration/saucy

« back to all changes in this revision

Viewing changes to src/main/java/org/apache/commons/configuration/tree/DefaultExpressionEngine.java

  • Committer: Package Import Robot
  • Author(s): Emmanuel Bourg
  • Date: 2013-07-01 16:29:44 UTC
  • mfrom: (1.1.4)
  • Revision ID: package-import@ubuntu.com-20130701162944-98waq5gogha5gpn1
Tags: 1.9-1
* New upstream release
* debian/control:
  - Updated Standards-Version to 3.9.4 (no changes)
  - Use canonical URLs for the Vcs-* fields
  - Added new build dependencies (libjavacc-maven-plugin-java, junit4)
  - Upgraded the dependency on the Servlet API (2.5 -> 3.0)
  - Removed the dependency on the Activation Framework (glassfish-activation)
  - Replaced the dependency on glassfish-mail with libgnumail-java
  - Removed the unused dependencies:
    liblog4j1.2-java-doc, libmaven-assembly-plugin-java
  - Replaced the dependency on libcommons-jexl-java by libcommons-jexl2-java
* debian/watch: Changed to point the official Apache distribution server
* Removed the obsolete file debian/ant.properties
* Installed the upstream changelog in the binary packages
* Added the report plugins to maven.ignoreRules
* Added the classpath attribute to the jar manifest

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
 
3
 * contributor license agreements.  See the NOTICE file distributed with
 
4
 * this work for additional information regarding copyright ownership.
 
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
 
6
 * (the "License"); you may not use this file except in compliance with
 
7
 * the License.  You may obtain a copy of the License at
 
8
 *
 
9
 *     http://www.apache.org/licenses/LICENSE-2.0
 
10
 *
 
11
 * Unless required by applicable law or agreed to in writing, software
 
12
 * distributed under the License is distributed on an "AS IS" BASIS,
 
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
14
 * See the License for the specific language governing permissions and
 
15
 * limitations under the License.
 
16
 */
 
17
package org.apache.commons.configuration.tree;
 
18
 
 
19
import java.util.Collection;
 
20
import java.util.LinkedList;
 
21
import java.util.List;
 
22
 
 
23
import org.apache.commons.lang.StringUtils;
 
24
 
 
25
/**
 
26
 * <p>
 
27
 * A default implementation of the {@code ExpressionEngine} interface
 
28
 * providing the &quot;native&quote; expression language for hierarchical
 
29
 * configurations.
 
30
 * </p>
 
31
 * <p>
 
32
 * This class implements a rather simple expression language for navigating
 
33
 * through a hierarchy of configuration nodes. It supports the following
 
34
 * operations:
 
35
 * </p>
 
36
 * <p>
 
37
 * <ul>
 
38
 * <li>Navigating from a node to one of its children using the child node
 
39
 * delimiter, which is by the default a dot (&quot;.&quot;).</li>
 
40
 * <li>Navigating from a node to one of its attributes using the attribute node
 
41
 * delimiter, which by default follows the XPATH like syntax
 
42
 * <code>[@&lt;attributeName&gt;]</code>.</li>
 
43
 * <li>If there are multiple child or attribute nodes with the same name, a
 
44
 * specific node can be selected using a numerical index. By default indices are
 
45
 * written in parenthesis.</li>
 
46
 * </ul>
 
47
 * </p>
 
48
 * <p>
 
49
 * As an example consider the following XML document:
 
50
 * </p>
 
51
 *
 
52
 * <pre>
 
53
 *  &lt;database&gt;
 
54
 *    &lt;tables&gt;
 
55
 *      &lt;table type=&quot;system&quot;&gt;
 
56
 *        &lt;name&gt;users&lt;/name&gt;
 
57
 *        &lt;fields&gt;
 
58
 *          &lt;field&gt;
 
59
 *            &lt;name&gt;lid&lt;/name&gt;
 
60
 *            &lt;type&gt;long&lt;/name&gt;
 
61
 *          &lt;/field&gt;
 
62
 *          &lt;field&gt;
 
63
 *            &lt;name&gt;usrName&lt;/name&gt;
 
64
 *            &lt;type&gt;java.lang.String&lt;/type&gt;
 
65
 *          &lt;/field&gt;
 
66
 *         ...
 
67
 *        &lt;/fields&gt;
 
68
 *      &lt;/table&gt;
 
69
 *      &lt;table&gt;
 
70
 *        &lt;name&gt;documents&lt;/name&gt;
 
71
 *        &lt;fields&gt;
 
72
 *          &lt;field&gt;
 
73
 *            &lt;name&gt;docid&lt;/name&gt;
 
74
 *            &lt;type&gt;long&lt;/type&gt;
 
75
 *          &lt;/field&gt;
 
76
 *          ...
 
77
 *        &lt;/fields&gt;
 
78
 *      &lt;/table&gt;
 
79
 *      ...
 
80
 *    &lt;/tables&gt;
 
81
 *  &lt;/database&gt;
 
82
 * </pre>
 
83
 *
 
84
 * </p>
 
85
 * <p>
 
86
 * If this document is parsed and stored in a hierarchical configuration object,
 
87
 * for instance the key {@code tables.table(0).name} can be used to find
 
88
 * out the name of the first table. In opposite {@code tables.table.name}
 
89
 * would return a collection with the names of all available tables. Similarly
 
90
 * the key {@code tables.table(1).fields.field.name} returns a collection
 
91
 * with the names of all fields of the second table. If another index is added
 
92
 * after the {@code field} element, a single field can be accessed:
 
93
 * {@code tables.table(1).fields.field(0).name}. The key
 
94
 * {@code tables.table(0)[@type]} would select the type attribute of the
 
95
 * first table.
 
96
 * </p>
 
97
 * <p>
 
98
 * This example works with the default values for delimiters and index markers.
 
99
 * It is also possible to set custom values for these properties so that you can
 
100
 * adapt a {@code DefaultExpressionEngine} to your personal needs.
 
101
 * </p>
 
102
 *
 
103
 * @since 1.3
 
104
 * @author <a
 
105
 * href="http://commons.apache.org/configuration/team-list.html">Commons
 
106
 * Configuration team</a>
 
107
 * @version $Id: DefaultExpressionEngine.java 1301991 2012-03-17 20:18:02Z sebb $
 
108
 */
 
109
public class DefaultExpressionEngine implements ExpressionEngine
 
110
{
 
111
    /** Constant for the default property delimiter. */
 
112
    public static final String DEFAULT_PROPERTY_DELIMITER = ".";
 
113
 
 
114
    /** Constant for the default escaped property delimiter. */
 
115
    public static final String DEFAULT_ESCAPED_DELIMITER = DEFAULT_PROPERTY_DELIMITER
 
116
            + DEFAULT_PROPERTY_DELIMITER;
 
117
 
 
118
    /** Constant for the default attribute start marker. */
 
119
    public static final String DEFAULT_ATTRIBUTE_START = "[@";
 
120
 
 
121
    /** Constant for the default attribute end marker. */
 
122
    public static final String DEFAULT_ATTRIBUTE_END = "]";
 
123
 
 
124
    /** Constant for the default index start marker. */
 
125
    public static final String DEFAULT_INDEX_START = "(";
 
126
 
 
127
    /** Constant for the default index end marker. */
 
128
    public static final String DEFAULT_INDEX_END = ")";
 
129
 
 
130
    /** Stores the property delimiter. */
 
131
    private String propertyDelimiter = DEFAULT_PROPERTY_DELIMITER;
 
132
 
 
133
    /** Stores the escaped property delimiter. */
 
134
    private String escapedDelimiter = DEFAULT_ESCAPED_DELIMITER;
 
135
 
 
136
    /** Stores the attribute start marker. */
 
137
    private String attributeStart = DEFAULT_ATTRIBUTE_START;
 
138
 
 
139
    /** Stores the attribute end marker. */
 
140
    private String attributeEnd = DEFAULT_ATTRIBUTE_END;
 
141
 
 
142
    /** Stores the index start marker. */
 
143
    private String indexStart = DEFAULT_INDEX_START;
 
144
 
 
145
    /** stores the index end marker. */
 
146
    private String indexEnd = DEFAULT_INDEX_END;
 
147
 
 
148
    /**
 
149
     * Sets the attribute end marker.
 
150
     *
 
151
     * @return the attribute end marker
 
152
     */
 
153
    public String getAttributeEnd()
 
154
    {
 
155
        return attributeEnd;
 
156
    }
 
157
 
 
158
    /**
 
159
     * Sets the attribute end marker.
 
160
     *
 
161
     * @param attributeEnd the attribute end marker; can be <b>null</b> if no
 
162
     * end marker is needed
 
163
     */
 
164
    public void setAttributeEnd(String attributeEnd)
 
165
    {
 
166
        this.attributeEnd = attributeEnd;
 
167
    }
 
168
 
 
169
    /**
 
170
     * Returns the attribute start marker.
 
171
     *
 
172
     * @return the attribute start marker
 
173
     */
 
174
    public String getAttributeStart()
 
175
    {
 
176
        return attributeStart;
 
177
    }
 
178
 
 
179
    /**
 
180
     * Sets the attribute start marker. Attribute start and end marker are used
 
181
     * together to detect attributes in a property key.
 
182
     *
 
183
     * @param attributeStart the attribute start marker
 
184
     */
 
185
    public void setAttributeStart(String attributeStart)
 
186
    {
 
187
        this.attributeStart = attributeStart;
 
188
    }
 
189
 
 
190
    /**
 
191
     * Returns the escaped property delimiter string.
 
192
     *
 
193
     * @return the escaped property delimiter
 
194
     */
 
195
    public String getEscapedDelimiter()
 
196
    {
 
197
        return escapedDelimiter;
 
198
    }
 
199
 
 
200
    /**
 
201
     * Sets the escaped property delimiter string. With this string a delimiter
 
202
     * that belongs to the key of a property can be escaped. If for instance
 
203
     * &quot;.&quot; is used as property delimiter, you can set the escaped
 
204
     * delimiter to &quot;\.&quot; and can then escape the delimiter with a back
 
205
     * slash.
 
206
     *
 
207
     * @param escapedDelimiter the escaped delimiter string
 
208
     */
 
209
    public void setEscapedDelimiter(String escapedDelimiter)
 
210
    {
 
211
        this.escapedDelimiter = escapedDelimiter;
 
212
    }
 
213
 
 
214
    /**
 
215
     * Returns the index end marker.
 
216
     *
 
217
     * @return the index end marker
 
218
     */
 
219
    public String getIndexEnd()
 
220
    {
 
221
        return indexEnd;
 
222
    }
 
223
 
 
224
    /**
 
225
     * Sets the index end marker.
 
226
     *
 
227
     * @param indexEnd the index end marker
 
228
     */
 
229
    public void setIndexEnd(String indexEnd)
 
230
    {
 
231
        this.indexEnd = indexEnd;
 
232
    }
 
233
 
 
234
    /**
 
235
     * Returns the index start marker.
 
236
     *
 
237
     * @return the index start marker
 
238
     */
 
239
    public String getIndexStart()
 
240
    {
 
241
        return indexStart;
 
242
    }
 
243
 
 
244
    /**
 
245
     * Sets the index start marker. Index start and end marker are used together
 
246
     * to detect indices in a property key.
 
247
     *
 
248
     * @param indexStart the index start marker
 
249
     */
 
250
    public void setIndexStart(String indexStart)
 
251
    {
 
252
        this.indexStart = indexStart;
 
253
    }
 
254
 
 
255
    /**
 
256
     * Returns the property delimiter.
 
257
     *
 
258
     * @return the property delimiter
 
259
     */
 
260
    public String getPropertyDelimiter()
 
261
    {
 
262
        return propertyDelimiter;
 
263
    }
 
264
 
 
265
    /**
 
266
     * Sets the property delimiter. This string is used to split the parts of a
 
267
     * property key.
 
268
     *
 
269
     * @param propertyDelimiter the property delimiter
 
270
     */
 
271
    public void setPropertyDelimiter(String propertyDelimiter)
 
272
    {
 
273
        this.propertyDelimiter = propertyDelimiter;
 
274
    }
 
275
 
 
276
    /**
 
277
     * Evaluates the given key and returns all matching nodes. This method
 
278
     * supports the syntax as described in the class comment.
 
279
     *
 
280
     * @param root the root node
 
281
     * @param key the key
 
282
     * @return a list with the matching nodes
 
283
     */
 
284
    public List<ConfigurationNode> query(ConfigurationNode root, String key)
 
285
    {
 
286
        List<ConfigurationNode> nodes = new LinkedList<ConfigurationNode>();
 
287
        findNodesForKey(new DefaultConfigurationKey(this, key).iterator(),
 
288
                root, nodes);
 
289
        return nodes;
 
290
    }
 
291
 
 
292
    /**
 
293
     * Determines the key of the passed in node. This implementation takes the
 
294
     * given parent key, adds a property delimiter, and then adds the node's
 
295
     * name. (For attribute nodes the attribute delimiters are used instead.)
 
296
     * The name of the root node is a blanc string. Note that no indices will be
 
297
     * returned.
 
298
     *
 
299
     * @param node the node whose key is to be determined
 
300
     * @param parentKey the key of this node's parent
 
301
     * @return the key for the given node
 
302
     */
 
303
    public String nodeKey(ConfigurationNode node, String parentKey)
 
304
    {
 
305
        if (parentKey == null)
 
306
        {
 
307
            // this is the root node
 
308
            return StringUtils.EMPTY;
 
309
        }
 
310
 
 
311
        else
 
312
        {
 
313
            DefaultConfigurationKey key = new DefaultConfigurationKey(this,
 
314
                    parentKey);
 
315
            if (node.isAttribute())
 
316
            {
 
317
                key.appendAttribute(node.getName());
 
318
            }
 
319
            else
 
320
            {
 
321
                key.append(node.getName(), true);
 
322
            }
 
323
            return key.toString();
 
324
        }
 
325
    }
 
326
 
 
327
    /**
 
328
     * <p>
 
329
     * Prepares Adding the property with the specified key.
 
330
     * </p>
 
331
     * <p>
 
332
     * To be able to deal with the structure supported by hierarchical
 
333
     * configuration implementations the passed in key is of importance,
 
334
     * especially the indices it might contain. The following example should
 
335
     * clarify this: Suppose the actual node structure looks like the
 
336
     * following:
 
337
     * </p>
 
338
     * <p>
 
339
     * <pre>
 
340
     *  tables
 
341
     *     +-- table
 
342
     *             +-- name = user
 
343
     *             +-- fields
 
344
     *                     +-- field
 
345
     *                             +-- name = uid
 
346
     *                     +-- field
 
347
     *                             +-- name = firstName
 
348
     *                     ...
 
349
     *     +-- table
 
350
     *             +-- name = documents
 
351
     *             +-- fields
 
352
     *                    ...
 
353
     * </pre>
 
354
     * </p>
 
355
     * <p>
 
356
     * In this example a database structure is defined, e.g. all fields of the
 
357
     * first table could be accessed using the key
 
358
     * {@code tables.table(0).fields.field.name}. If now properties are
 
359
     * to be added, it must be exactly specified at which position in the
 
360
     * hierarchy the new property is to be inserted. So to add a new field name
 
361
     * to a table it is not enough to say just
 
362
     * </p>
 
363
     * <p>
 
364
     * <pre>
 
365
     * config.addProperty(&quot;tables.table.fields.field.name&quot;, &quot;newField&quot;);
 
366
     * </pre>
 
367
     * </p>
 
368
     * <p>
 
369
     * The statement given above contains some ambiguity. For instance it is not
 
370
     * clear, to which table the new field should be added. If this method finds
 
371
     * such an ambiguity, it is resolved by following the last valid path. Here
 
372
     * this would be the last table. The same is true for the {@code field};
 
373
     * because there are multiple fields and no explicit index is provided, a
 
374
     * new {@code name} property would be added to the last field - which
 
375
     * is probably not what was desired.
 
376
     * </p>
 
377
     * <p>
 
378
     * To make things clear explicit indices should be provided whenever
 
379
     * possible. In the example above the exact table could be specified by
 
380
     * providing an index for the {@code table} element as in
 
381
     * {@code tables.table(1).fields}. By specifying an index it can
 
382
     * also be expressed that at a given position in the configuration tree a
 
383
     * new branch should be added. In the example above we did not want to add
 
384
     * an additional {@code name} element to the last field of the table,
 
385
     * but we want a complete new {@code field} element. This can be
 
386
     * achieved by specifying an invalid index (like -1) after the element where
 
387
     * a new branch should be created. Given this our example would run:
 
388
     * </p>
 
389
     * <p>
 
390
     * <pre>
 
391
     * config.addProperty(&quot;tables.table(1).fields.field(-1).name&quot;, &quot;newField&quot;);
 
392
     * </pre>
 
393
     * </p>
 
394
     * <p>
 
395
     * With this notation it is possible to add new branches everywhere. We
 
396
     * could for instance create a new {@code table} element by
 
397
     * specifying
 
398
     * </p>
 
399
     * <p>
 
400
     * <pre>
 
401
     * config.addProperty(&quot;tables.table(-1).fields.field.name&quot;, &quot;newField2&quot;);
 
402
     * </pre>
 
403
     * </p>
 
404
     * <p>
 
405
     * (Note that because after the {@code table} element a new branch is
 
406
     * created indices in following elements are not relevant; the branch is new
 
407
     * so there cannot be any ambiguities.)
 
408
     * </p>
 
409
     *
 
410
     * @param root the root node of the nodes hierarchy
 
411
     * @param key the key of the new property
 
412
     * @return a data object with information needed for the add operation
 
413
     */
 
414
    public NodeAddData prepareAdd(ConfigurationNode root, String key)
 
415
    {
 
416
        DefaultConfigurationKey.KeyIterator it = new DefaultConfigurationKey(
 
417
                this, key).iterator();
 
418
        if (!it.hasNext())
 
419
        {
 
420
            throw new IllegalArgumentException(
 
421
                    "Key for add operation must be defined!");
 
422
        }
 
423
 
 
424
        NodeAddData result = new NodeAddData();
 
425
        result.setParent(findLastPathNode(it, root));
 
426
 
 
427
        while (it.hasNext())
 
428
        {
 
429
            if (!it.isPropertyKey())
 
430
            {
 
431
                throw new IllegalArgumentException(
 
432
                        "Invalid key for add operation: " + key
 
433
                                + " (Attribute key in the middle.)");
 
434
            }
 
435
            result.addPathNode(it.currentKey());
 
436
            it.next();
 
437
        }
 
438
 
 
439
        result.setNewNodeName(it.currentKey());
 
440
        result.setAttribute(!it.isPropertyKey());
 
441
        return result;
 
442
    }
 
443
 
 
444
    /**
 
445
     * Recursive helper method for evaluating a key. This method processes all
 
446
     * facets of a configuration key, traverses the tree of properties and
 
447
     * fetches the the nodes of all matching properties.
 
448
     *
 
449
     * @param keyPart the configuration key iterator
 
450
     * @param node the actual node
 
451
     * @param nodes here the found nodes are stored
 
452
     */
 
453
    protected void findNodesForKey(DefaultConfigurationKey.KeyIterator keyPart,
 
454
            ConfigurationNode node, Collection<ConfigurationNode> nodes)
 
455
    {
 
456
        if (!keyPart.hasNext())
 
457
        {
 
458
            nodes.add(node);
 
459
        }
 
460
 
 
461
        else
 
462
        {
 
463
            String key = keyPart.nextKey(false);
 
464
            if (keyPart.isPropertyKey())
 
465
            {
 
466
                processSubNodes(keyPart, node.getChildren(key), nodes);
 
467
            }
 
468
            if (keyPart.isAttribute())
 
469
            {
 
470
                processSubNodes(keyPart, node.getAttributes(key), nodes);
 
471
            }
 
472
        }
 
473
    }
 
474
 
 
475
    /**
 
476
     * Finds the last existing node for an add operation. This method traverses
 
477
     * the configuration node tree along the specified key. The last existing
 
478
     * node on this path is returned.
 
479
     *
 
480
     * @param keyIt the key iterator
 
481
     * @param node the actual node
 
482
     * @return the last existing node on the given path
 
483
     */
 
484
    protected ConfigurationNode findLastPathNode(
 
485
            DefaultConfigurationKey.KeyIterator keyIt, ConfigurationNode node)
 
486
    {
 
487
        String keyPart = keyIt.nextKey(false);
 
488
 
 
489
        if (keyIt.hasNext())
 
490
        {
 
491
            if (!keyIt.isPropertyKey())
 
492
            {
 
493
                // Attribute keys can only appear as last elements of the path
 
494
                throw new IllegalArgumentException(
 
495
                        "Invalid path for add operation: "
 
496
                                + "Attribute key in the middle!");
 
497
            }
 
498
            int idx = keyIt.hasIndex() ? keyIt.getIndex() : node
 
499
                    .getChildrenCount(keyPart) - 1;
 
500
            if (idx < 0 || idx >= node.getChildrenCount(keyPart))
 
501
            {
 
502
                return node;
 
503
            }
 
504
            else
 
505
            {
 
506
                return findLastPathNode(keyIt, node.getChildren(keyPart).get(idx));
 
507
            }
 
508
        }
 
509
 
 
510
        else
 
511
        {
 
512
            return node;
 
513
        }
 
514
    }
 
515
 
 
516
    /**
 
517
     * Called by {@code findNodesForKey()} to process the sub nodes of
 
518
     * the current node depending on the type of the current key part (children,
 
519
     * attributes, or both).
 
520
     *
 
521
     * @param keyPart the key part
 
522
     * @param subNodes a list with the sub nodes to process
 
523
     * @param nodes the target collection
 
524
     */
 
525
    private void processSubNodes(DefaultConfigurationKey.KeyIterator keyPart,
 
526
            List<ConfigurationNode> subNodes, Collection<ConfigurationNode> nodes)
 
527
    {
 
528
        if (keyPart.hasIndex())
 
529
        {
 
530
            if (keyPart.getIndex() >= 0 && keyPart.getIndex() < subNodes.size())
 
531
            {
 
532
                findNodesForKey((DefaultConfigurationKey.KeyIterator) keyPart
 
533
                        .clone(), subNodes.get(keyPart.getIndex()), nodes);
 
534
            }
 
535
        }
 
536
        else
 
537
        {
 
538
            for (ConfigurationNode node : subNodes)
 
539
            {
 
540
                findNodesForKey((DefaultConfigurationKey.KeyIterator) keyPart
 
541
                        .clone(), node, nodes);
 
542
            }
 
543
        }
 
544
    }
 
545
}