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

« back to all changes in this revision

Viewing changes to src/main/java/org/apache/commons/configuration/ConfigurationKey.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
 
 
18
package org.apache.commons.configuration;
 
19
 
 
20
import java.io.Serializable;
 
21
import java.util.Iterator;
 
22
import java.util.NoSuchElementException;
 
23
 
 
24
/**
 
25
 * <p>A simple class that supports creation of and iteration on complex
 
26
 * configuration keys.</p>
 
27
 *
 
28
 * <p>For key creation the class works similar to a StringBuilder: There are
 
29
 * several {@code appendXXXX()} methods with which single parts
 
30
 * of a key can be constructed. All these methods return a reference to the
 
31
 * actual object so they can be written in a chain. When using this methods
 
32
 * the exact syntax for keys need not be known.</p>
 
33
 *
 
34
 * <p>This class also defines a specialized iterator for configuration keys.
 
35
 * With such an iterator a key can be tokenized into its single parts. For
 
36
 * each part it can be checked whether it has an associated index.</p>
 
37
 *
 
38
 * @author <a
 
39
 * href="http://commons.apache.org/configuration/team-list.html">Commons
 
40
 * Configuration team</a>
 
41
 * @version $Id: ConfigurationKey.java 1231749 2012-01-15 20:48:56Z oheger $
 
42
 * @deprecated Use {@link org.apache.commons.configuration.tree.DefaultConfigurationKey}
 
43
 * instead. It is associated with a {@code DefaultExpressionEngine} and thus
 
44
 * can produce correct keys even if key separators have been changed.
 
45
 */
 
46
@Deprecated
 
47
public class ConfigurationKey implements Serializable
 
48
{
 
49
    /** Constant for a property delimiter.*/
 
50
    public static final char PROPERTY_DELIMITER = '.';
 
51
 
 
52
    /** Constant for an escaped delimiter. */
 
53
    public static final String ESCAPED_DELIMITER =
 
54
        String.valueOf(PROPERTY_DELIMITER) + String.valueOf(PROPERTY_DELIMITER);
 
55
 
 
56
    /** Constant for an attribute start marker.*/
 
57
    private static final String ATTRIBUTE_START = "[@";
 
58
 
 
59
    /** Constant for an attribute end marker.*/
 
60
    private static final String ATTRIBUTE_END = "]";
 
61
 
 
62
    /** Constant for an index start marker.*/
 
63
    private static final char INDEX_START = '(';
 
64
 
 
65
    /** Constant for an index end marker.*/
 
66
    private static final char INDEX_END = ')';
 
67
 
 
68
    /** Constant for the initial StringBuilder size.*/
 
69
    private static final int INITIAL_SIZE = 32;
 
70
 
 
71
    /**
 
72
     * The serial version ID.
 
73
     */
 
74
    private static final long serialVersionUID = -4299732083605277656L;
 
75
 
 
76
    /** Holds a buffer with the so far created key.*/
 
77
    private StringBuilder keyBuffer;
 
78
 
 
79
    /**
 
80
     * Creates a new, empty instance of {@code ConfigurationKey}.
 
81
     */
 
82
    public ConfigurationKey()
 
83
    {
 
84
        keyBuffer = new StringBuilder(INITIAL_SIZE);
 
85
    }
 
86
 
 
87
    /**
 
88
     * Creates a new instance of {@code ConfigurationKey} and
 
89
     * initializes it with the given key.
 
90
     *
 
91
     * @param key the key as a string
 
92
     */
 
93
    public ConfigurationKey(String key)
 
94
    {
 
95
        keyBuffer = new StringBuilder(key);
 
96
        removeTrailingDelimiter();
 
97
    }
 
98
 
 
99
    /**
 
100
     * Appends the name of a property to this key. If necessary, a
 
101
     * property delimiter will be added.
 
102
     *
 
103
     * @param property the name of the property to be added
 
104
     * @return a reference to this object
 
105
     */
 
106
    public ConfigurationKey append(String property)
 
107
    {
 
108
        if (keyBuffer.length() > 0 && !hasDelimiter() && !isAttributeKey(property))
 
109
        {
 
110
            keyBuffer.append(PROPERTY_DELIMITER);
 
111
        }
 
112
 
 
113
        keyBuffer.append(property);
 
114
        removeTrailingDelimiter();
 
115
        return this;
 
116
    }
 
117
 
 
118
    /**
 
119
     * Appends an index to this configuration key.
 
120
     *
 
121
     * @param index the index to be appended
 
122
     * @return a reference to this object
 
123
     */
 
124
    public ConfigurationKey appendIndex(int index)
 
125
    {
 
126
        keyBuffer.append(INDEX_START).append(index);
 
127
        keyBuffer.append(INDEX_END);
 
128
        return this;
 
129
    }
 
130
 
 
131
    /**
 
132
     * Appends an attribute to this configuration key.
 
133
     *
 
134
     * @param attr the name of the attribute to be appended
 
135
     * @return a reference to this object
 
136
     */
 
137
    public ConfigurationKey appendAttribute(String attr)
 
138
    {
 
139
        keyBuffer.append(constructAttributeKey(attr));
 
140
        return this;
 
141
    }
 
142
 
 
143
    /**
 
144
     * Checks if this key is an attribute key.
 
145
     *
 
146
     * @return a flag if this key is an attribute key
 
147
     */
 
148
    public boolean isAttributeKey()
 
149
    {
 
150
        return isAttributeKey(keyBuffer.toString());
 
151
    }
 
152
 
 
153
    /**
 
154
     * Checks if the passed in key is an attribute key. Such attribute keys
 
155
     * start and end with certain marker strings. In some cases they must be
 
156
     * treated slightly different.
 
157
     *
 
158
     * @param key the key (part) to be checked
 
159
     * @return a flag if this key is an attribute key
 
160
     */
 
161
    public static boolean isAttributeKey(String key)
 
162
    {
 
163
        return key != null
 
164
        && key.startsWith(ATTRIBUTE_START)
 
165
        && key.endsWith(ATTRIBUTE_END);
 
166
    }
 
167
 
 
168
    /**
 
169
     * Decorates the given key so that it represents an attribute. Adds
 
170
     * special start and end markers.
 
171
     *
 
172
     * @param key the key to be decorated
 
173
     * @return the decorated attribute key
 
174
     */
 
175
    public static String constructAttributeKey(String key)
 
176
    {
 
177
        StringBuilder buf = new StringBuilder();
 
178
        buf.append(ATTRIBUTE_START).append(key).append(ATTRIBUTE_END);
 
179
        return buf.toString();
 
180
    }
 
181
 
 
182
    /**
 
183
     * Extracts the name of the attribute from the given attribute key.
 
184
     * This method removes the attribute markers - if any - from the
 
185
     * specified key.
 
186
     *
 
187
     * @param key the attribute key
 
188
     * @return the name of the corresponding attribute
 
189
     */
 
190
    public static String attributeName(String key)
 
191
    {
 
192
        return isAttributeKey(key) ? removeAttributeMarkers(key) : key;
 
193
    }
 
194
 
 
195
    /**
 
196
     * Helper method for removing attribute markers from a key.
 
197
     *
 
198
     * @param key the key
 
199
     * @return the key with removed attribute markers
 
200
     */
 
201
    static String removeAttributeMarkers(String key)
 
202
    {
 
203
        return key.substring(ATTRIBUTE_START.length(), key.length() - ATTRIBUTE_END.length());
 
204
    }
 
205
 
 
206
    /**
 
207
     * Helper method that checks if the actual buffer ends with a property
 
208
     * delimiter.
 
209
     *
 
210
     * @return a flag if there is a trailing delimiter
 
211
     */
 
212
    private boolean hasDelimiter()
 
213
    {
 
214
        int count = 0;
 
215
        for (int idx = keyBuffer.length() - 1; idx >= 0
 
216
                && keyBuffer.charAt(idx) == PROPERTY_DELIMITER; idx--)
 
217
        {
 
218
            count++;
 
219
        }
 
220
        return count % 2 != 0;
 
221
    }
 
222
 
 
223
    /**
 
224
     * Removes a trailing delimiter if there is any.
 
225
     */
 
226
    private void removeTrailingDelimiter()
 
227
    {
 
228
        while (hasDelimiter())
 
229
        {
 
230
            keyBuffer.deleteCharAt(keyBuffer.length() - 1);
 
231
        }
 
232
    }
 
233
 
 
234
    /**
 
235
     * Returns a string representation of this object. This is the
 
236
     * configuration key as a plain string.
 
237
     *
 
238
     * @return a string for this object
 
239
     */
 
240
    @Override
 
241
    public String toString()
 
242
    {
 
243
        return keyBuffer.toString();
 
244
    }
 
245
 
 
246
    /**
 
247
     * Returns an iterator for iterating over the single components of
 
248
     * this configuration key.
 
249
     *
 
250
     * @return an iterator for this key
 
251
     */
 
252
    public KeyIterator iterator()
 
253
    {
 
254
        return new KeyIterator();
 
255
    }
 
256
 
 
257
    /**
 
258
     * Returns the actual length of this configuration key.
 
259
     *
 
260
     * @return the length of this key
 
261
     */
 
262
    public int length()
 
263
    {
 
264
        return keyBuffer.length();
 
265
    }
 
266
 
 
267
    /**
 
268
     * Sets the new length of this configuration key. With this method it is
 
269
     * possible to truncate the key, e.g. to return to a state prior calling
 
270
     * some {@code append()} methods. The semantic is the same as
 
271
     * the {@code setLength()} method of {@code StringBuilder}.
 
272
     *
 
273
     * @param len the new length of the key
 
274
     */
 
275
    public void setLength(int len)
 
276
    {
 
277
        keyBuffer.setLength(len);
 
278
    }
 
279
 
 
280
    /**
 
281
     * Checks if two {@code ConfigurationKey} objects are equal. The
 
282
     * method can be called with strings or other objects, too.
 
283
     *
 
284
     * @param c the object to compare
 
285
     * @return a flag if both objects are equal
 
286
     */
 
287
    @Override
 
288
    public boolean equals(Object c)
 
289
    {
 
290
        if (c == null)
 
291
        {
 
292
            return false;
 
293
        }
 
294
 
 
295
        return keyBuffer.toString().equals(c.toString());
 
296
    }
 
297
 
 
298
    /**
 
299
     * Returns the hash code for this object.
 
300
     *
 
301
     * @return the hash code
 
302
     */
 
303
    @Override
 
304
    public int hashCode()
 
305
    {
 
306
        return String.valueOf(keyBuffer).hashCode();
 
307
    }
 
308
 
 
309
    /**
 
310
     * Returns a configuration key object that is initialized with the part
 
311
     * of the key that is common to this key and the passed in key.
 
312
     *
 
313
     * @param other the other key
 
314
     * @return a key object with the common key part
 
315
     */
 
316
    public ConfigurationKey commonKey(ConfigurationKey other)
 
317
    {
 
318
        if (other == null)
 
319
        {
 
320
            throw new IllegalArgumentException("Other key must no be null!");
 
321
        }
 
322
 
 
323
        ConfigurationKey result = new ConfigurationKey();
 
324
        KeyIterator it1 = iterator();
 
325
        KeyIterator it2 = other.iterator();
 
326
 
 
327
        while (it1.hasNext() && it2.hasNext() && partsEqual(it1, it2))
 
328
        {
 
329
            if (it1.isAttribute())
 
330
            {
 
331
                result.appendAttribute(it1.currentKey());
 
332
            }
 
333
            else
 
334
            {
 
335
                result.append(it1.currentKey());
 
336
                if (it1.hasIndex)
 
337
                {
 
338
                    result.appendIndex(it1.getIndex());
 
339
                }
 
340
            }
 
341
        }
 
342
 
 
343
        return result;
 
344
    }
 
345
 
 
346
    /**
 
347
     * Returns the &quot;difference key&quot; to a given key. This value
 
348
     * is the part of the passed in key that differs from this key. There is
 
349
     * the following relation:
 
350
     * {@code other = key.commonKey(other) + key.differenceKey(other)}
 
351
     * for an arbitrary configuration key {@code key}.
 
352
     *
 
353
     * @param other the key for which the difference is to be calculated
 
354
     * @return the difference key
 
355
     */
 
356
    public ConfigurationKey differenceKey(ConfigurationKey other)
 
357
    {
 
358
        ConfigurationKey common = commonKey(other);
 
359
        ConfigurationKey result = new ConfigurationKey();
 
360
 
 
361
        if (common.length() < other.length())
 
362
        {
 
363
            String k = other.toString().substring(common.length());
 
364
            // skip trailing delimiters
 
365
            int i = 0;
 
366
            while (i < k.length() && k.charAt(i) == PROPERTY_DELIMITER)
 
367
            {
 
368
                i++;
 
369
            }
 
370
 
 
371
            if (i < k.length())
 
372
            {
 
373
                result.append(k.substring(i));
 
374
            }
 
375
        }
 
376
 
 
377
        return result;
 
378
    }
 
379
 
 
380
    /**
 
381
     * Helper method for comparing two key parts.
 
382
     *
 
383
     * @param it1 the iterator with the first part
 
384
     * @param it2 the iterator with the second part
 
385
     * @return a flag if both parts are equal
 
386
     */
 
387
    private static boolean partsEqual(KeyIterator it1, KeyIterator it2)
 
388
    {
 
389
        return it1.nextKey().equals(it2.nextKey())
 
390
        && it1.getIndex() == it2.getIndex()
 
391
        && it1.isAttribute() == it2.isAttribute();
 
392
    }
 
393
 
 
394
    /**
 
395
     * A specialized iterator class for tokenizing a configuration key.
 
396
     * This class implements the normal iterator interface. In addition it
 
397
     * provides some specific methods for configuration keys.
 
398
     */
 
399
    public class KeyIterator implements Iterator<Object>, Cloneable
 
400
    {
 
401
        /** Stores the current key name.*/
 
402
        private String current;
 
403
 
 
404
        /** Stores the start index of the actual token.*/
 
405
        private int startIndex;
 
406
 
 
407
        /** Stores the end index of the actual token.*/
 
408
        private int endIndex;
 
409
 
 
410
        /** Stores the index of the actual property if there is one.*/
 
411
        private int indexValue;
 
412
 
 
413
        /** Stores a flag if the actual property has an index.*/
 
414
        private boolean hasIndex;
 
415
 
 
416
        /** Stores a flag if the actual property is an attribute.*/
 
417
        private boolean attribute;
 
418
 
 
419
        /**
 
420
         * Helper method for determining the next indices.
 
421
         *
 
422
         * @return the next key part
 
423
         */
 
424
        private String findNextIndices()
 
425
        {
 
426
            startIndex = endIndex;
 
427
            // skip empty names
 
428
            while (startIndex < keyBuffer.length()
 
429
                    && keyBuffer.charAt(startIndex) == PROPERTY_DELIMITER)
 
430
            {
 
431
                startIndex++;
 
432
            }
 
433
 
 
434
            // Key ends with a delimiter?
 
435
            if (startIndex >= keyBuffer.length())
 
436
            {
 
437
                endIndex = keyBuffer.length();
 
438
                startIndex = endIndex - 1;
 
439
                return keyBuffer.substring(startIndex, endIndex);
 
440
            }
 
441
            else
 
442
            {
 
443
                return nextKeyPart();
 
444
            }
 
445
        }
 
446
 
 
447
        /**
 
448
         * Helper method for extracting the next key part. Takes escaping of
 
449
         * delimiter characters into account.
 
450
         *
 
451
         * @return the next key part
 
452
         */
 
453
        private String nextKeyPart()
 
454
        {
 
455
            StringBuilder key = new StringBuilder(INITIAL_SIZE);
 
456
            int idx = startIndex;
 
457
            int endIdx = keyBuffer.toString().indexOf(ATTRIBUTE_START,
 
458
                    startIndex);
 
459
            if (endIdx < 0 || endIdx == startIndex)
 
460
            {
 
461
                endIdx = keyBuffer.length();
 
462
            }
 
463
            boolean found = false;
 
464
 
 
465
            while (!found && idx < endIdx)
 
466
            {
 
467
                char c = keyBuffer.charAt(idx);
 
468
                if (c == PROPERTY_DELIMITER)
 
469
                {
 
470
                    // a duplicated delimiter means escaping
 
471
                    if (idx == endIdx - 1
 
472
                            || keyBuffer.charAt(idx + 1) != PROPERTY_DELIMITER)
 
473
                    {
 
474
                        found = true;
 
475
                    }
 
476
                    else
 
477
                    {
 
478
                        idx++;
 
479
                    }
 
480
                }
 
481
                if (!found)
 
482
                {
 
483
                    key.append(c);
 
484
                    idx++;
 
485
                }
 
486
            }
 
487
 
 
488
            endIndex = idx;
 
489
            return key.toString();
 
490
        }
 
491
 
 
492
        /**
 
493
         * Returns the next key part of this configuration key. This is a short
 
494
         * form of {@code nextKey(false)}.
 
495
         *
 
496
         * @return the next key part
 
497
         */
 
498
        public String nextKey()
 
499
        {
 
500
            return nextKey(false);
 
501
        }
 
502
 
 
503
        /**
 
504
         * Returns the next key part of this configuration key. The boolean
 
505
         * parameter indicates wheter a decorated key should be returned. This
 
506
         * affects only attribute keys: if the parameter is <b>false</b>, the
 
507
         * attribute markers are stripped from the key; if it is <b>true</b>,
 
508
         * they remain.
 
509
         *
 
510
         * @param decorated a flag if the decorated key is to be returned
 
511
         * @return the next key part
 
512
         */
 
513
        public String nextKey(boolean decorated)
 
514
        {
 
515
            if (!hasNext())
 
516
            {
 
517
                throw new NoSuchElementException("No more key parts!");
 
518
            }
 
519
 
 
520
            hasIndex = false;
 
521
            indexValue = -1;
 
522
            String key = findNextIndices();
 
523
 
 
524
            current = key;
 
525
            hasIndex = checkIndex(key);
 
526
            attribute = checkAttribute(current);
 
527
 
 
528
            return currentKey(decorated);
 
529
        }
 
530
 
 
531
        /**
 
532
         * Helper method for checking if the passed key is an attribute.
 
533
         * If this is the case, the internal fields will be set.
 
534
         *
 
535
         * @param key the key to be checked
 
536
         * @return a flag if the key is an attribute
 
537
         */
 
538
        private boolean checkAttribute(String key)
 
539
        {
 
540
            if (isAttributeKey(key))
 
541
            {
 
542
                current = removeAttributeMarkers(key);
 
543
                return true;
 
544
            }
 
545
            else
 
546
            {
 
547
                return false;
 
548
            }
 
549
        }
 
550
 
 
551
        /**
 
552
         * Helper method for checking if the passed key contains an index.
 
553
         * If this is the case, internal fields will be set.
 
554
         *
 
555
         * @param key the key to be checked
 
556
         * @return a flag if an index is defined
 
557
         */
 
558
        private boolean checkIndex(String key)
 
559
        {
 
560
            boolean result = false;
 
561
 
 
562
            int idx = key.lastIndexOf(INDEX_START);
 
563
            if (idx > 0)
 
564
            {
 
565
                int endidx = key.indexOf(INDEX_END, idx);
 
566
 
 
567
                if (endidx > idx + 1)
 
568
                {
 
569
                    indexValue = Integer.parseInt(key.substring(idx + 1, endidx));
 
570
                    current = key.substring(0, idx);
 
571
                    result = true;
 
572
                }
 
573
            }
 
574
 
 
575
            return result;
 
576
        }
 
577
 
 
578
        /**
 
579
         * Checks if there is a next element.
 
580
         *
 
581
         * @return a flag if there is a next element
 
582
         */
 
583
        public boolean hasNext()
 
584
        {
 
585
            return endIndex < keyBuffer.length();
 
586
        }
 
587
 
 
588
        /**
 
589
         * Returns the next object in the iteration.
 
590
         *
 
591
         * @return the next object
 
592
         */
 
593
        public Object next()
 
594
        {
 
595
            return nextKey();
 
596
        }
 
597
 
 
598
        /**
 
599
         * Removes the current object in the iteration. This method is not
 
600
         * supported by this iterator type, so an exception is thrown.
 
601
         */
 
602
        public void remove()
 
603
        {
 
604
            throw new UnsupportedOperationException("Remove not supported!");
 
605
        }
 
606
 
 
607
        /**
 
608
         * Returns the current key of the iteration (without skipping to the
 
609
         * next element). This is the same key the previous {@code next()}
 
610
         * call had returned. (Short form of {@code currentKey(false)}.
 
611
         *
 
612
         * @return the current key
 
613
         */
 
614
        public String currentKey()
 
615
        {
 
616
            return currentKey(false);
 
617
        }
 
618
 
 
619
        /**
 
620
         * Returns the current key of the iteration (without skipping to the
 
621
         * next element). The boolean parameter indicates wheter a decorated
 
622
         * key should be returned. This affects only attribute keys: if the
 
623
         * parameter is <b>false</b>, the attribute markers are stripped from
 
624
         * the key; if it is <b>true</b>, they remain.
 
625
         *
 
626
         * @param decorated a flag if the decorated key is to be returned
 
627
         * @return the current key
 
628
         */
 
629
        public String currentKey(boolean decorated)
 
630
        {
 
631
            return (decorated && isAttribute()) ? constructAttributeKey(current) : current;
 
632
        }
 
633
 
 
634
        /**
 
635
         * Returns a flag if the current key is an attribute. This method can
 
636
         * be called after {@code next()}.
 
637
         *
 
638
         * @return a flag if the current key is an attribute
 
639
         */
 
640
        public boolean isAttribute()
 
641
        {
 
642
            return attribute;
 
643
        }
 
644
 
 
645
        /**
 
646
         * Returns the index value of the current key. If the current key does
 
647
         * not have an index, return value is -1. This method can be called
 
648
         * after {@code next()}.
 
649
         *
 
650
         * @return the index value of the current key
 
651
         */
 
652
        public int getIndex()
 
653
        {
 
654
            return indexValue;
 
655
        }
 
656
 
 
657
        /**
 
658
         * Returns a flag if the current key has an associated index.
 
659
         * This method can be called after {@code next()}.
 
660
         *
 
661
         * @return a flag if the current key has an index
 
662
         */
 
663
        public boolean hasIndex()
 
664
        {
 
665
            return hasIndex;
 
666
        }
 
667
 
 
668
        /**
 
669
         * Creates a clone of this object.
 
670
         *
 
671
         * @return a clone of this object
 
672
         */
 
673
        @Override
 
674
        public Object clone()
 
675
        {
 
676
            try
 
677
            {
 
678
                return super.clone();
 
679
            }
 
680
            catch (CloneNotSupportedException cex)
 
681
            {
 
682
                // should not happen
 
683
                return null;
 
684
            }
 
685
        }
 
686
    }
 
687
}