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     */
017    package org.apache.commons.configuration;
018    
019    import java.math.BigDecimal;
020    import java.math.BigInteger;
021    import java.util.ArrayList;
022    import java.util.Collection;
023    import java.util.HashMap;
024    import java.util.Iterator;
025    import java.util.List;
026    import java.util.Map;
027    import java.util.Properties;
028    import java.util.Set;
029    
030    import org.apache.commons.configuration.event.ConfigurationErrorListener;
031    import org.apache.commons.configuration.event.ConfigurationListener;
032    import org.apache.commons.configuration.interpol.ConfigurationInterpolator;
033    import org.apache.commons.configuration.tree.ConfigurationNode;
034    import org.apache.commons.configuration.tree.ExpressionEngine;
035    import org.apache.commons.configuration.tree.NodeCombiner;
036    import org.apache.commons.lang.text.StrSubstitutor;
037    import org.apache.commons.logging.Log;
038    import org.apache.commons.logging.LogFactory;
039    
040    /**
041     * DynamicCombinedConfiguration allows a set of CombinedConfigurations to be used. Each CombinedConfiguration
042     * is referenced by a key that is dynamically constructed from a key pattern on each call. The key pattern
043     * will be resolved using the configured ConfigurationInterpolator.
044     * @since 1.6
045     * @author <a
046     * href="http://commons.apache.org/configuration/team-list.html">Commons
047     * Configuration team</a>
048     * @version $Id: DynamicCombinedConfiguration.java 1231721 2012-01-15 18:32:07Z oheger $
049     */
050    public class DynamicCombinedConfiguration extends CombinedConfiguration
051    {
052        /**
053         * Prevent recursion while resolving unprefixed properties.
054         */
055        private static ThreadLocal<Boolean> recursive = new ThreadLocal<Boolean>()
056        {
057            @Override
058            protected synchronized Boolean initialValue()
059            {
060                return Boolean.FALSE;
061            }
062        };
063    
064        /** The CombinedConfigurations */
065        private Map<String, CombinedConfiguration> configs =
066                new HashMap<String, CombinedConfiguration>();
067    
068        /** Stores a list with the contained configurations. */
069        private List<ConfigData> configurations = new ArrayList<ConfigData>();
070    
071        /** Stores a map with the named configurations. */
072        private Map<String, AbstractConfiguration> namedConfigurations =
073                new HashMap<String, AbstractConfiguration>();
074    
075        /** The key pattern for the CombinedConfiguration map */
076        private String keyPattern;
077    
078        /** Stores the combiner. */
079        private NodeCombiner nodeCombiner;
080    
081        /** The name of the logger to use for each CombinedConfiguration */
082        private String loggerName = DynamicCombinedConfiguration.class.getName();
083    
084        /** The object for handling variable substitution in key patterns. */
085        private StrSubstitutor localSubst = new StrSubstitutor(new ConfigurationInterpolator());
086    
087        /**
088         * Creates a new instance of {@code DynamicCombinedConfiguration} and
089         * initializes the combiner to be used.
090         *
091         * @param comb the node combiner (can be <b>null</b>, then a union combiner
092         * is used as default)
093         */
094        public DynamicCombinedConfiguration(NodeCombiner comb)
095        {
096            super();
097            setNodeCombiner(comb);
098            setIgnoreReloadExceptions(false);
099            setLogger(LogFactory.getLog(DynamicCombinedConfiguration.class));
100        }
101    
102        /**
103         * Creates a new instance of {@code DynamicCombinedConfiguration} that uses
104         * a union combiner.
105         *
106         * @see org.apache.commons.configuration.tree.UnionCombiner
107         */
108        public DynamicCombinedConfiguration()
109        {
110            super();
111            setIgnoreReloadExceptions(false);
112            setLogger(LogFactory.getLog(DynamicCombinedConfiguration.class));
113        }
114    
115        public void setKeyPattern(String pattern)
116        {
117            this.keyPattern = pattern;
118        }
119    
120        public String getKeyPattern()
121        {
122            return this.keyPattern;
123        }
124    
125        /**
126         * Set the name of the Logger to use on each CombinedConfiguration.
127         * @param name The Logger name.
128         */
129        public void setLoggerName(String name)
130        {
131            this.loggerName = name;
132        }
133    
134        /**
135         * Returns the node combiner that is used for creating the combined node
136         * structure.
137         *
138         * @return the node combiner
139         */
140        @Override
141        public NodeCombiner getNodeCombiner()
142        {
143            return nodeCombiner;
144        }
145    
146        /**
147         * Sets the node combiner. This object will be used when the combined node
148         * structure is to be constructed. It must not be <b>null</b>, otherwise an
149         * {@code IllegalArgumentException} exception is thrown. Changing the
150         * node combiner causes an invalidation of this combined configuration, so
151         * that the new combiner immediately takes effect.
152         *
153         * @param nodeCombiner the node combiner
154         */
155        @Override
156        public void setNodeCombiner(NodeCombiner nodeCombiner)
157        {
158            if (nodeCombiner == null)
159            {
160                throw new IllegalArgumentException(
161                        "Node combiner must not be null!");
162            }
163            this.nodeCombiner = nodeCombiner;
164            invalidateAll();
165        }
166        /**
167         * Adds a new configuration to this combined configuration. It is possible
168         * (but not mandatory) to give the new configuration a name. This name must
169         * be unique, otherwise a {@code ConfigurationRuntimeException} will
170         * be thrown. With the optional {@code at} argument you can specify
171         * where in the resulting node structure the content of the added
172         * configuration should appear. This is a string that uses dots as property
173         * delimiters (independent on the current expression engine). For instance
174         * if you pass in the string {@code "database.tables"},
175         * all properties of the added configuration will occur in this branch.
176         *
177         * @param config the configuration to add (must not be <b>null</b>)
178         * @param name the name of this configuration (can be <b>null</b>)
179         * @param at the position of this configuration in the combined tree (can be
180         * <b>null</b>)
181         */
182        @Override
183        public void addConfiguration(AbstractConfiguration config, String name,
184                String at)
185        {
186            ConfigData cd = new ConfigData(config, name, at);
187            configurations.add(cd);
188            if (name != null)
189            {
190                namedConfigurations.put(name, config);
191            }
192        }
193           /**
194         * Returns the number of configurations that are contained in this combined
195         * configuration.
196         *
197         * @return the number of contained configurations
198         */
199        @Override
200        public int getNumberOfConfigurations()
201        {
202            return configurations.size();
203        }
204    
205        /**
206         * Returns the configuration at the specified index. The contained
207         * configurations are numbered in the order they were added to this combined
208         * configuration. The index of the first configuration is 0.
209         *
210         * @param index the index
211         * @return the configuration at this index
212         */
213        @Override
214        public Configuration getConfiguration(int index)
215        {
216            ConfigData cd = configurations.get(index);
217            return cd.getConfiguration();
218        }
219    
220        /**
221         * Returns the configuration with the given name. This can be <b>null</b>
222         * if no such configuration exists.
223         *
224         * @param name the name of the configuration
225         * @return the configuration with this name
226         */
227        @Override
228        public Configuration getConfiguration(String name)
229        {
230            return namedConfigurations.get(name);
231        }
232    
233        /**
234         * Returns a set with the names of all configurations contained in this
235         * combined configuration. Of course here are only these configurations
236         * listed, for which a name was specified when they were added.
237         *
238         * @return a set with the names of the contained configurations (never
239         * <b>null</b>)
240         */
241        @Override
242        public Set<String> getConfigurationNames()
243        {
244            return namedConfigurations.keySet();
245        }
246    
247        /**
248         * Removes the configuration with the specified name.
249         *
250         * @param name the name of the configuration to be removed
251         * @return the removed configuration (<b>null</b> if this configuration
252         * was not found)
253         */
254        @Override
255        public Configuration removeConfiguration(String name)
256        {
257            Configuration conf = getConfiguration(name);
258            if (conf != null)
259            {
260                removeConfiguration(conf);
261            }
262            return conf;
263        }
264    
265        /**
266         * Removes the specified configuration from this combined configuration.
267         *
268         * @param config the configuration to be removed
269         * @return a flag whether this configuration was found and could be removed
270         */
271        @Override
272        public boolean removeConfiguration(Configuration config)
273        {
274            for (int index = 0; index < getNumberOfConfigurations(); index++)
275            {
276                if (configurations.get(index).getConfiguration() == config)
277                {
278                    removeConfigurationAt(index);
279    
280                }
281            }
282    
283            return super.removeConfiguration(config);
284        }
285    
286        /**
287         * Removes the configuration at the specified index.
288         *
289         * @param index the index
290         * @return the removed configuration
291         */
292        @Override
293        public Configuration removeConfigurationAt(int index)
294        {
295            ConfigData cd = configurations.remove(index);
296            if (cd.getName() != null)
297            {
298                namedConfigurations.remove(cd.getName());
299            }
300            return super.removeConfigurationAt(index);
301        }
302        /**
303         * Returns the configuration root node of this combined configuration. This
304         * method will construct a combined node structure using the current node
305         * combiner if necessary.
306         *
307         * @return the combined root node
308         */
309        @Override
310        public ConfigurationNode getRootNode()
311        {
312            return getCurrentConfig().getRootNode();
313        }
314    
315        @Override
316        public void setRootNode(ConfigurationNode rootNode)
317        {
318            if (configs != null)
319            {
320                this.getCurrentConfig().setRootNode(rootNode);
321            }
322            else
323            {
324                super.setRootNode(rootNode);
325            }
326        }
327    
328        @Override
329        public void addProperty(String key, Object value)
330        {
331            this.getCurrentConfig().addProperty(key, value);
332        }
333    
334        @Override
335        public void clear()
336        {
337            if (configs != null)
338            {
339                this.getCurrentConfig().clear();
340            }
341        }
342    
343        @Override
344        public void clearProperty(String key)
345        {
346            this.getCurrentConfig().clearProperty(key);
347        }
348    
349        @Override
350        public boolean containsKey(String key)
351        {
352            return this.getCurrentConfig().containsKey(key);
353        }
354    
355        @Override
356        public BigDecimal getBigDecimal(String key, BigDecimal defaultValue)
357        {
358            return this.getCurrentConfig().getBigDecimal(key, defaultValue);
359        }
360    
361        @Override
362        public BigDecimal getBigDecimal(String key)
363        {
364            return this.getCurrentConfig().getBigDecimal(key);
365        }
366    
367        @Override
368        public BigInteger getBigInteger(String key, BigInteger defaultValue)
369        {
370            return this.getCurrentConfig().getBigInteger(key, defaultValue);
371        }
372    
373        @Override
374        public BigInteger getBigInteger(String key)
375        {
376            return this.getCurrentConfig().getBigInteger(key);
377        }
378    
379        @Override
380        public boolean getBoolean(String key, boolean defaultValue)
381        {
382            return this.getCurrentConfig().getBoolean(key, defaultValue);
383        }
384    
385        @Override
386        public Boolean getBoolean(String key, Boolean defaultValue)
387        {
388            return this.getCurrentConfig().getBoolean(key, defaultValue);
389        }
390    
391        @Override
392        public boolean getBoolean(String key)
393        {
394            return this.getCurrentConfig().getBoolean(key);
395        }
396    
397        @Override
398        public byte getByte(String key, byte defaultValue)
399        {
400            return this.getCurrentConfig().getByte(key, defaultValue);
401        }
402    
403        @Override
404        public Byte getByte(String key, Byte defaultValue)
405        {
406            return this.getCurrentConfig().getByte(key, defaultValue);
407        }
408    
409        @Override
410        public byte getByte(String key)
411        {
412            return this.getCurrentConfig().getByte(key);
413        }
414    
415        @Override
416        public double getDouble(String key, double defaultValue)
417        {
418            return this.getCurrentConfig().getDouble(key, defaultValue);
419        }
420    
421        @Override
422        public Double getDouble(String key, Double defaultValue)
423        {
424            return this.getCurrentConfig().getDouble(key, defaultValue);
425        }
426    
427        @Override
428        public double getDouble(String key)
429        {
430            return this.getCurrentConfig().getDouble(key);
431        }
432    
433        @Override
434        public float getFloat(String key, float defaultValue)
435        {
436            return this.getCurrentConfig().getFloat(key, defaultValue);
437        }
438    
439        @Override
440        public Float getFloat(String key, Float defaultValue)
441        {
442            return this.getCurrentConfig().getFloat(key, defaultValue);
443        }
444    
445        @Override
446        public float getFloat(String key)
447        {
448            return this.getCurrentConfig().getFloat(key);
449        }
450    
451        @Override
452        public int getInt(String key, int defaultValue)
453        {
454            return this.getCurrentConfig().getInt(key, defaultValue);
455        }
456    
457        @Override
458        public int getInt(String key)
459        {
460            return this.getCurrentConfig().getInt(key);
461        }
462    
463        @Override
464        public Integer getInteger(String key, Integer defaultValue)
465        {
466            return this.getCurrentConfig().getInteger(key, defaultValue);
467        }
468    
469        @Override
470        public Iterator<String> getKeys()
471        {
472            return this.getCurrentConfig().getKeys();
473        }
474    
475        @Override
476        public Iterator<String> getKeys(String prefix)
477        {
478            return this.getCurrentConfig().getKeys(prefix);
479        }
480    
481        @Override
482        public List<Object> getList(String key, List<Object> defaultValue)
483        {
484            return this.getCurrentConfig().getList(key, defaultValue);
485        }
486    
487        @Override
488        public List<Object> getList(String key)
489        {
490            return this.getCurrentConfig().getList(key);
491        }
492    
493        @Override
494        public long getLong(String key, long defaultValue)
495        {
496            return this.getCurrentConfig().getLong(key, defaultValue);
497        }
498    
499        @Override
500        public Long getLong(String key, Long defaultValue)
501        {
502            return this.getCurrentConfig().getLong(key, defaultValue);
503        }
504    
505        @Override
506        public long getLong(String key)
507        {
508            return this.getCurrentConfig().getLong(key);
509        }
510    
511        @Override
512        public Properties getProperties(String key)
513        {
514            return this.getCurrentConfig().getProperties(key);
515        }
516    
517        @Override
518        public Object getProperty(String key)
519        {
520            return this.getCurrentConfig().getProperty(key);
521        }
522    
523        @Override
524        public short getShort(String key, short defaultValue)
525        {
526            return this.getCurrentConfig().getShort(key, defaultValue);
527        }
528    
529        @Override
530        public Short getShort(String key, Short defaultValue)
531        {
532            return this.getCurrentConfig().getShort(key, defaultValue);
533        }
534    
535        @Override
536        public short getShort(String key)
537        {
538            return this.getCurrentConfig().getShort(key);
539        }
540    
541        @Override
542        public String getString(String key, String defaultValue)
543        {
544            return this.getCurrentConfig().getString(key, defaultValue);
545        }
546    
547        @Override
548        public String getString(String key)
549        {
550            return this.getCurrentConfig().getString(key);
551        }
552    
553        @Override
554        public String[] getStringArray(String key)
555        {
556            return this.getCurrentConfig().getStringArray(key);
557        }
558    
559        @Override
560        public boolean isEmpty()
561        {
562            return this.getCurrentConfig().isEmpty();
563        }
564    
565        @Override
566        public void setProperty(String key, Object value)
567        {
568            if (configs != null)
569            {
570                this.getCurrentConfig().setProperty(key, value);
571            }
572        }
573    
574        @Override
575        public Configuration subset(String prefix)
576        {
577            return this.getCurrentConfig().subset(prefix);
578        }
579    
580        @Override
581        public Node getRoot()
582        {
583            return this.getCurrentConfig().getRoot();
584        }
585    
586        @Override
587        public void setRoot(Node node)
588        {
589            if (configs != null)
590            {
591                this.getCurrentConfig().setRoot(node);
592            }
593            else
594            {
595                super.setRoot(node);
596            }
597        }
598    
599        @Override
600        public ExpressionEngine getExpressionEngine()
601        {
602            return super.getExpressionEngine();
603        }
604    
605        @Override
606        public void setExpressionEngine(ExpressionEngine expressionEngine)
607        {
608            super.setExpressionEngine(expressionEngine);
609        }
610    
611        @Override
612        public void addNodes(String key, Collection<? extends ConfigurationNode> nodes)
613        {
614            this.getCurrentConfig().addNodes(key, nodes);
615        }
616    
617        @Override
618        public SubnodeConfiguration configurationAt(String key, boolean supportUpdates)
619        {
620            return this.getCurrentConfig().configurationAt(key, supportUpdates);
621        }
622    
623        @Override
624        public SubnodeConfiguration configurationAt(String key)
625        {
626            return this.getCurrentConfig().configurationAt(key);
627        }
628    
629        @Override
630        public List<HierarchicalConfiguration> configurationsAt(String key)
631        {
632            return this.getCurrentConfig().configurationsAt(key);
633        }
634    
635        @Override
636        public void clearTree(String key)
637        {
638            this.getCurrentConfig().clearTree(key);
639        }
640    
641        @Override
642        public int getMaxIndex(String key)
643        {
644            return this.getCurrentConfig().getMaxIndex(key);
645        }
646    
647        @Override
648        public Configuration interpolatedConfiguration()
649        {
650            return this.getCurrentConfig().interpolatedConfiguration();
651        }
652    
653    
654        /**
655         * Returns the configuration source, in which the specified key is defined.
656         * This method will determine the configuration node that is identified by
657         * the given key. The following constellations are possible:
658         * <ul>
659         * <li>If no node object is found for this key, <b>null</b> is returned.</li>
660         * <li>If the key maps to multiple nodes belonging to different
661         * configuration sources, a {@code IllegalArgumentException} is
662         * thrown (in this case no unique source can be determined).</li>
663         * <li>If exactly one node is found for the key, the (child) configuration
664         * object, to which the node belongs is determined and returned.</li>
665         * <li>For keys that have been added directly to this combined
666         * configuration and that do not belong to the namespaces defined by
667         * existing child configurations this configuration will be returned.</li>
668         * </ul>
669         *
670         * @param key the key of a configuration property
671         * @return the configuration, to which this property belongs or <b>null</b>
672         * if the key cannot be resolved
673         * @throws IllegalArgumentException if the key maps to multiple properties
674         * and the source cannot be determined, or if the key is <b>null</b>
675         */
676        @Override
677        public Configuration getSource(String key)
678        {
679            if (key == null)
680            {
681                throw new IllegalArgumentException("Key must not be null!");
682            }
683            return getCurrentConfig().getSource(key);
684        }
685    
686        @Override
687        public void addConfigurationListener(ConfigurationListener l)
688        {
689            super.addConfigurationListener(l);
690    
691            for (CombinedConfiguration cc : configs.values())
692            {
693                cc.addConfigurationListener(l);
694            }
695        }
696    
697        @Override
698        public boolean removeConfigurationListener(ConfigurationListener l)
699        {
700            for (CombinedConfiguration cc : configs.values())
701            {
702                cc.removeConfigurationListener(l);
703            }
704            return super.removeConfigurationListener(l);
705        }
706    
707        @Override
708        public Collection<ConfigurationListener> getConfigurationListeners()
709        {
710            return super.getConfigurationListeners();
711        }
712    
713        @Override
714        public void clearConfigurationListeners()
715        {
716            for (CombinedConfiguration cc : configs.values())
717            {
718                cc.clearConfigurationListeners();
719            }
720            super.clearConfigurationListeners();
721        }
722    
723        @Override
724        public void addErrorListener(ConfigurationErrorListener l)
725        {
726            for (CombinedConfiguration cc : configs.values())
727            {
728                cc.addErrorListener(l);
729            }
730            super.addErrorListener(l);
731        }
732    
733        @Override
734        public boolean removeErrorListener(ConfigurationErrorListener l)
735        {
736            for (CombinedConfiguration cc : configs.values())
737            {
738                cc.removeErrorListener(l);
739            }
740            return super.removeErrorListener(l);
741        }
742    
743        @Override
744        public void clearErrorListeners()
745        {
746            for (CombinedConfiguration cc : configs.values())
747            {
748                cc.clearErrorListeners();
749            }
750            super.clearErrorListeners();
751        }
752    
753        @Override
754        public Collection<ConfigurationErrorListener> getErrorListeners()
755        {
756            return super.getErrorListeners();
757        }
758    
759        /**
760         * Returns a copy of this object. This implementation performs a deep clone,
761         * i.e. all contained configurations will be cloned, too. For this to work,
762         * all contained configurations must be cloneable. Registered event
763         * listeners won't be cloned. The clone will use the same node combiner than
764         * the original.
765         *
766         * @return the copied object
767         */
768        @Override
769        public Object clone()
770        {
771            return super.clone();
772        }
773    
774        /**
775         * Invalidates the current combined configuration. This means that the next time a
776         * property is accessed the combined node structure must be re-constructed.
777         * Invalidation of a combined configuration also means that an event of type
778         * {@code EVENT_COMBINED_INVALIDATE} is fired. Note that while other
779         * events most times appear twice (once before and once after an update),
780         * this event is only fired once (after update).
781         */
782        @Override
783        public void invalidate()
784        {
785            getCurrentConfig().invalidate();
786        }
787    
788        public void invalidateAll()
789        {
790            if (configs == null)
791            {
792                return;
793            }
794            for (CombinedConfiguration cc : configs.values())
795            {
796                cc.invalidate();
797            }
798        }
799    
800        /*
801         * Don't allow resolveContainerStore to be called recursively.
802         * @param key The key to resolve.
803         * @return The value of the key.
804         */
805        @Override
806        protected Object resolveContainerStore(String key)
807        {
808            if (recursive.get().booleanValue())
809            {
810                return null;
811            }
812            recursive.set(Boolean.TRUE);
813            try
814            {
815                return super.resolveContainerStore(key);
816            }
817            finally
818            {
819                recursive.set(Boolean.FALSE);
820            }
821        }
822    
823        private CombinedConfiguration getCurrentConfig()
824        {
825            String key = localSubst.replace(keyPattern);
826            CombinedConfiguration config;
827            synchronized (getNodeCombiner())
828            {
829                config = configs.get(key);
830                if (config == null)
831                {
832                    config = new CombinedConfiguration(getNodeCombiner());
833                    if (loggerName != null)
834                    {
835                        Log log = LogFactory.getLog(loggerName);
836                        if (log != null)
837                        {
838                            config.setLogger(log);
839                        }
840                    }
841                    config.setIgnoreReloadExceptions(isIgnoreReloadExceptions());
842                    config.setExpressionEngine(this.getExpressionEngine());
843                    config.setDelimiterParsingDisabled(isDelimiterParsingDisabled());
844                    config.setConversionExpressionEngine(getConversionExpressionEngine());
845                    config.setListDelimiter(getListDelimiter());
846                    for (ConfigurationErrorListener listener : getErrorListeners())
847                    {
848                        config.addErrorListener(listener);
849                    }
850                    for (ConfigurationListener listener : getConfigurationListeners())
851                    {
852                        config.addConfigurationListener(listener);
853                    }
854                    config.setForceReloadCheck(isForceReloadCheck());
855                    for (ConfigData data : configurations)
856                    {
857                        config.addConfiguration(data.getConfiguration(), data.getName(),
858                                data.getAt());
859                    }
860                    configs.put(key, config);
861                }
862            }
863            if (getLogger().isDebugEnabled())
864            {
865                getLogger().debug("Returning config for " + key + ": " + config);
866            }
867            return config;
868        }
869    
870        /**
871         * Internal class that identifies each Configuration.
872         */
873        static class ConfigData
874        {
875            /** Stores a reference to the configuration. */
876            private AbstractConfiguration configuration;
877    
878            /** Stores the name under which the configuration is stored. */
879            private String name;
880    
881            /** Stores the at string.*/
882            private String at;
883    
884            /**
885             * Creates a new instance of {@code ConfigData} and initializes
886             * it.
887             *
888             * @param config the configuration
889             * @param n the name
890             * @param at the at position
891             */
892            public ConfigData(AbstractConfiguration config, String n, String at)
893            {
894                configuration = config;
895                name = n;
896                this.at = at;
897            }
898    
899            /**
900             * Returns the stored configuration.
901             *
902             * @return the configuration
903             */
904            public AbstractConfiguration getConfiguration()
905            {
906                return configuration;
907            }
908    
909            /**
910             * Returns the configuration's name.
911             *
912             * @return the name
913             */
914            public String getName()
915            {
916                return name;
917            }
918    
919            /**
920             * Returns the at position of this configuration.
921             *
922             * @return the at position
923             */
924            public String getAt()
925            {
926                return at;
927            }
928    
929        }
930    }