001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.configuration2.tree;
018
019import org.apache.commons.lang3.builder.EqualsBuilder;
020import org.apache.commons.lang3.builder.HashCodeBuilder;
021import org.apache.commons.lang3.builder.ToStringBuilder;
022
023/**
024 * <p>
025 * A class representing the various symbols that are supported in keys recognized by {@link DefaultExpressionEngine}.
026 * </p>
027 * <p>
028 * An instance of this class is associated with each instance of {@code DefaultExpressionEngine}. It determines which
029 * concrete symbols are used to define elements like separators, attributes, etc. within a configuration key.
030 * </p>
031 * <p>
032 * Instances are created using the nested {@code Builder} class. They are immutable and can be shared between arbitrary
033 * components.
034 * </p>
035 *
036 * @since 2.0
037 */
038public final class DefaultExpressionEngineSymbols {
039    /** Constant for the default property delimiter. */
040    public static final String DEFAULT_PROPERTY_DELIMITER = ".";
041
042    /** Constant for the default escaped property delimiter. */
043    public static final String DEFAULT_ESCAPED_DELIMITER = DEFAULT_PROPERTY_DELIMITER + DEFAULT_PROPERTY_DELIMITER;
044
045    /** Constant for the default attribute start marker. */
046    public static final String DEFAULT_ATTRIBUTE_START = "[@";
047
048    /** Constant for the default attribute end marker. */
049    public static final String DEFAULT_ATTRIBUTE_END = "]";
050
051    /** Constant for the default index start marker. */
052    public static final String DEFAULT_INDEX_START = "(";
053
054    /** Constant for the default index end marker. */
055    public static final String DEFAULT_INDEX_END = ")";
056
057    /**
058     * An instance with default symbols. This instance is used by the default instance of {@code DefaultExpressionEngine}.
059     */
060    public static final DefaultExpressionEngineSymbols DEFAULT_SYMBOLS = createDefaultSmybols();
061
062    /** Stores the property delimiter. */
063    private final String propertyDelimiter;
064
065    /** Stores the escaped property delimiter. */
066    private final String escapedDelimiter;
067
068    /** Stores the attribute start marker. */
069    private final String attributeStart;
070
071    /** Stores the attribute end marker. */
072    private final String attributeEnd;
073
074    /** Stores the index start marker. */
075    private final String indexStart;
076
077    /** Stores the index end marker. */
078    private final String indexEnd;
079
080    /**
081     * Creates a new instance of {@code DefaultExpressionEngineSymbols}.
082     *
083     * @param b the builder for defining the properties of this instance
084     */
085    private DefaultExpressionEngineSymbols(final Builder b) {
086        propertyDelimiter = b.propertyDelimiter;
087        escapedDelimiter = b.escapedDelimiter;
088        indexStart = b.indexStart;
089        indexEnd = b.indexEnd;
090        attributeStart = b.attributeStart;
091        attributeEnd = b.attributeEnd;
092    }
093
094    /**
095     * Gets the string used as delimiter in property keys.
096     *
097     * @return the property delimiter
098     */
099    public String getPropertyDelimiter() {
100        return propertyDelimiter;
101    }
102
103    /**
104     * Gets the string representing an escaped property delimiter.
105     *
106     * @return the escaped property delimiter
107     */
108    public String getEscapedDelimiter() {
109        return escapedDelimiter;
110    }
111
112    /**
113     * Gets the string representing an attribute start marker.
114     *
115     * @return the attribute start marker
116     */
117    public String getAttributeStart() {
118        return attributeStart;
119    }
120
121    /**
122     * Gets the string representing an attribute end marker.
123     *
124     * @return the attribute end marker
125     */
126    public String getAttributeEnd() {
127        return attributeEnd;
128    }
129
130    /**
131     * Gets the string representing the start of an index in a property key.
132     *
133     * @return the index start marker
134     */
135    public String getIndexStart() {
136        return indexStart;
137    }
138
139    /**
140     * Gets the string representing the end of an index in a property key.
141     *
142     * @return the index end marker
143     */
144    public String getIndexEnd() {
145        return indexEnd;
146    }
147
148    /**
149     * Returns a hash code for this object.
150     *
151     * @return a hash code
152     */
153    @Override
154    public int hashCode() {
155        return new HashCodeBuilder().append(getPropertyDelimiter()).append(getEscapedDelimiter()).append(getIndexStart()).append(getIndexEnd())
156            .append(getAttributeStart()).append(getAttributeEnd()).toHashCode();
157    }
158
159    /**
160     * Compares this object with another one. Two instances of {@code DefaultExpressionEngineSymbols} are considered equal
161     * if all of their properties are equal.
162     *
163     * @param obj the object to compare to
164     * @return a flag whether these objects are equal
165     */
166    @Override
167    public boolean equals(final Object obj) {
168        if (this == obj) {
169            return true;
170        }
171        if (!(obj instanceof DefaultExpressionEngineSymbols)) {
172            return false;
173        }
174
175        final DefaultExpressionEngineSymbols c = (DefaultExpressionEngineSymbols) obj;
176        return new EqualsBuilder().append(getPropertyDelimiter(), c.getPropertyDelimiter()).append(getEscapedDelimiter(), c.getEscapedDelimiter())
177            .append(getIndexStart(), c.getIndexStart()).append(getIndexEnd(), c.getIndexEnd()).append(getAttributeStart(), c.getAttributeStart())
178            .append(getAttributeEnd(), c.getAttributeEnd()).isEquals();
179    }
180
181    /**
182     * Returns a string representation for this object. This string contains the values of all properties.
183     *
184     * @return a string for this object
185     */
186    @Override
187    public String toString() {
188        return new ToStringBuilder(this).append("propertyDelimiter", getPropertyDelimiter()).append("escapedDelimiter", getEscapedDelimiter())
189            .append("indexStart", getIndexStart()).append("indexEnd", getIndexEnd()).append("attributeStart", getAttributeStart())
190            .append("attributeEnd", getAttributeEnd()).toString();
191    }
192
193    /**
194     * Creates the {@code DefaultExpressionEngineSymbols} object with default symbols.
195     *
196     * @return the default symbols instance
197     */
198    private static DefaultExpressionEngineSymbols createDefaultSmybols() {
199        return new Builder().setPropertyDelimiter(DEFAULT_PROPERTY_DELIMITER).setEscapedDelimiter(DEFAULT_ESCAPED_DELIMITER).setIndexStart(DEFAULT_INDEX_START)
200            .setIndexEnd(DEFAULT_INDEX_END).setAttributeStart(DEFAULT_ATTRIBUTE_START).setAttributeEnd(DEFAULT_ATTRIBUTE_END).create();
201    }
202
203    /**
204     * A builder class for creating instances of {@code DefaultExpressionEngineSymbols}.
205     */
206    public static class Builder {
207        /** Stores the property delimiter. */
208        private String propertyDelimiter;
209
210        /** Stores the escaped property delimiter. */
211        private String escapedDelimiter;
212
213        /** Stores the attribute start marker. */
214        private String attributeStart;
215
216        /** Stores the attribute end marker. */
217        private String attributeEnd;
218
219        /** Stores the index start marker. */
220        private String indexStart;
221
222        /** Stores the index end marker. */
223        private String indexEnd;
224
225        /**
226         * Creates a new, uninitialized instance of {@code Builder}. All symbols are undefined.
227         */
228        public Builder() {
229        }
230
231        /**
232         * Creates a new instance of {@code Builder} whose properties are initialized from the passed in
233         * {@code DefaultExpressionEngineSymbols} object. This is useful if symbols are to be created which are similar to the
234         * passed in instance.
235         *
236         * @param c the {@code DefaultExpressionEngineSymbols} object serving as starting point for this builder
237         */
238        public Builder(final DefaultExpressionEngineSymbols c) {
239            propertyDelimiter = c.getPropertyDelimiter();
240            escapedDelimiter = c.getEscapedDelimiter();
241            indexStart = c.getIndexStart();
242            indexEnd = c.getIndexEnd();
243            attributeStart = c.getAttributeStart();
244            attributeEnd = c.getAttributeEnd();
245        }
246
247        /**
248         * Sets the string representing a delimiter for properties.
249         *
250         * @param propertyDelimiter the property delimiter
251         * @return a reference to this object for method chaining
252         */
253        public Builder setPropertyDelimiter(final String propertyDelimiter) {
254            this.propertyDelimiter = propertyDelimiter;
255            return this;
256        }
257
258        /**
259         * Sets the string representing an escaped property delimiter. With this string a delimiter that belongs to the key of a
260         * property can be escaped. If for instance &quot;.&quot; is used as property delimiter, you can set the escaped
261         * delimiter to &quot;\.&quot; and can then escape the delimiter with a back slash.
262         *
263         * @param escapedDelimiter the escaped property delimiter
264         * @return a reference to this object for method chaining
265         */
266        public Builder setEscapedDelimiter(final String escapedDelimiter) {
267            this.escapedDelimiter = escapedDelimiter;
268            return this;
269        }
270
271        /**
272         * Sets the string representing the start of an index in a property key. Index start and end marker are used together to
273         * detect indices in a property key.
274         *
275         * @param is the index start
276         * @return a reference to this object for method chaining
277         */
278        public Builder setIndexStart(final String is) {
279            this.indexStart = is;
280            return this;
281        }
282
283        /**
284         * Sets the string representing the end of an index in a property key.
285         *
286         * @param indexEnd the index end
287         * @return a reference to this object for method chaining
288         */
289        public Builder setIndexEnd(final String indexEnd) {
290            this.indexEnd = indexEnd;
291            return this;
292        }
293
294        /**
295         * Sets the string representing the start marker of an attribute in a property key. Attribute start and end marker are
296         * used together to detect attributes in a property key.
297         *
298         * @param attributeStart the attribute start marker
299         * @return a reference to this object for method chaining
300         */
301        public Builder setAttributeStart(final String attributeStart) {
302            this.attributeStart = attributeStart;
303            return this;
304        }
305
306        /**
307         * Sets the string representing the end marker of an attribute in a property key.
308         *
309         * @param attributeEnd the attribute end marker
310         * @return a reference to this object for method chaining
311         */
312        public Builder setAttributeEnd(final String attributeEnd) {
313            this.attributeEnd = attributeEnd;
314            return this;
315        }
316
317        /**
318         * Creates the {@code DefaultExpressionEngineSymbols} instance based on the properties set for this builder object. This
319         * method does not change the state of this builder. So it is possible to change properties and create another
320         * {@code DefaultExpressionEngineSymbols} instance.
321         *
322         * @return the newly created {@code DefaultExpressionEngineSymbols} instance
323         */
324        public DefaultExpressionEngineSymbols create() {
325            return new DefaultExpressionEngineSymbols(this);
326        }
327    }
328}