Source Code Cross Referenced for PropertiesConfiguration.java in  » Library » Apache-commons-configuration-1.4-src » org » apache » commons » configuration » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Library » Apache commons configuration 1.4 src » org.apache.commons.configuration 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * Licensed to the Apache Software Foundation (ASF) under one or more
0003:         * contributor license agreements.  See the NOTICE file distributed with
0004:         * this work for additional information regarding copyright ownership.
0005:         * The ASF licenses this file to You under the Apache License, Version 2.0
0006:         * (the "License"); you may not use this file except in compliance with
0007:         * the License.  You may obtain a copy of the License at
0008:         *
0009:         *     http://www.apache.org/licenses/LICENSE-2.0
0010:         *
0011:         * Unless required by applicable law or agreed to in writing, software
0012:         * distributed under the License is distributed on an "AS IS" BASIS,
0013:         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014:         * See the License for the specific language governing permissions and
0015:         * limitations under the License.
0016:         */
0017:
0018:        package org.apache.commons.configuration;
0019:
0020:        import java.io.File;
0021:        import java.io.FilterWriter;
0022:        import java.io.IOException;
0023:        import java.io.LineNumberReader;
0024:        import java.io.Reader;
0025:        import java.io.Writer;
0026:        import java.net.URL;
0027:        import java.util.ArrayList;
0028:        import java.util.Iterator;
0029:        import java.util.List;
0030:
0031:        import org.apache.commons.lang.ArrayUtils;
0032:        import org.apache.commons.lang.StringEscapeUtils;
0033:        import org.apache.commons.lang.StringUtils;
0034:
0035:        /**
0036:         * This is the "classic" Properties loader which loads the values from
0037:         * a single or multiple files (which can be chained with "include =".
0038:         * All given path references are either absolute or relative to the
0039:         * file name supplied in the constructor.
0040:         * <p>
0041:         * In this class, empty PropertyConfigurations can be built, properties
0042:         * added and later saved. include statements are (obviously) not supported
0043:         * if you don't construct a PropertyConfiguration from a file.
0044:         *
0045:         * <p>The properties file syntax is explained here, basically it follows
0046:         * the syntax of the stream parsed by {@link java.util.Properties#load} and
0047:         * adds several useful extensions:
0048:         *
0049:         * <ul>
0050:         *  <li>
0051:         *   Each property has the syntax <code>key &lt;separator> value</code>. The
0052:         *   separators accepted are <code>'='</code>, <code>':'</code> and any white
0053:         *   space character. Examples:
0054:         * <pre>
0055:         *  key1 = value1
0056:         *  key2 : value2
0057:         *  key3   value3</pre>
0058:         *  </li>
0059:         *  <li>
0060:         *   The <i>key</i> may use any character, separators must be escaped:
0061:         * <pre>
0062:         *  key\:foo = bar</pre>
0063:         *  </li>
0064:         *  <li>
0065:         *   <i>value</i> may be separated on different lines if a backslash
0066:         *   is placed at the end of the line that continues below.
0067:         *  </li>
0068:         *  <li>
0069:         *   <i>value</i> can contain <em>value delimiters</em> and will then be interpreted
0070:         *   as a list of tokens. Default value delimiter is the comma ','. So the
0071:         *   following property definition
0072:         * <pre>
0073:         *  key = This property, has multiple, values
0074:         * </pre>
0075:         *   will result in a property with three values. You can change the value
0076:         *   delimiter using the <code>{@link AbstractConfiguration#setListDelimiter(char)}</code>
0077:         *   method. Setting the delimiter to 0 will disable value splitting completely.
0078:         *  </li>
0079:         *  <li>
0080:         *   Commas in each token are escaped placing a backslash right before
0081:         *   the comma.
0082:         *  </li>
0083:         *  <li>
0084:         *   If a <i>key</i> is used more than once, the values are appended
0085:         *   like if they were on the same line separated with commas.
0086:         *  </li>
0087:         *  <li>
0088:         *   Blank lines and lines starting with character '#' or '!' are skipped.
0089:         *  </li>
0090:         *  <li>
0091:         *   If a property is named "include" (or whatever is defined by
0092:         *   setInclude() and getInclude() and the value of that property is
0093:         *   the full path to a file on disk, that file will be included into
0094:         *   the configuration. You can also pull in files relative to the parent
0095:         *   configuration file. So if you have something like the following:
0096:         *
0097:         *   include = additional.properties
0098:         *
0099:         *   Then "additional.properties" is expected to be in the same
0100:         *   directory as the parent configuration file.
0101:         *
0102:         *   The properties in the included file are added to the parent configuration,
0103:         *   they do not replace existing properties with the same key.
0104:         *
0105:         *  </li>
0106:         * </ul>
0107:         *
0108:         * <p>Here is an example of a valid extended properties file:
0109:         *
0110:         * <p><pre>
0111:         *      # lines starting with # are comments
0112:         *
0113:         *      # This is the simplest property
0114:         *      key = value
0115:         *
0116:         *      # A long property may be separated on multiple lines
0117:         *      longvalue = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \
0118:         *                  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
0119:         *
0120:         *      # This is a property with many tokens
0121:         *      tokens_on_a_line = first token, second token
0122:         *
0123:         *      # This sequence generates exactly the same result
0124:         *      tokens_on_multiple_lines = first token
0125:         *      tokens_on_multiple_lines = second token
0126:         *
0127:         *      # commas may be escaped in tokens
0128:         *      commas.escaped = Hi\, what'up?
0129:         *
0130:         *      # properties can reference other properties
0131:         *      base.prop = /base
0132:         *      first.prop = ${base.prop}/first
0133:         *      second.prop = ${first.prop}/second
0134:         * </pre>
0135:         *
0136:         * <p>A <code>PropertiesConfiguration</code> object is associated with an
0137:         * instance of the <code>{@link PropertiesConfigurationLayout}</code> class,
0138:         * which is responsible for storing the layout of the parsed properties file
0139:         * (i.e. empty lines, comments, and such things). The <code>getLayout()</code>
0140:         * method can be used to obtain this layout object. With <code>setLayout()</code>
0141:         * a new layout object can be set. This should be done before a properties file
0142:         * was loaded.
0143:         *
0144:         * @see java.util.Properties#load
0145:         *
0146:         * @author <a href="mailto:stefano@apache.org">Stefano Mazzocchi</a>
0147:         * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
0148:         * @author <a href="mailto:daveb@miceda-data">Dave Bryson</a>
0149:         * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
0150:         * @author <a href="mailto:leon@opticode.co.za">Leon Messerschmidt</a>
0151:         * @author <a href="mailto:kjohnson@transparent.com">Kent Johnson</a>
0152:         * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
0153:         * @author <a href="mailto:ipriha@surfeu.fi">Ilkka Priha</a>
0154:         * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
0155:         * @author <a href="mailto:mpoeschl@marmot.at">Martin Poeschl</a>
0156:         * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
0157:         * @author <a href="mailto:epugh@upstate.com">Eric Pugh</a>
0158:         * @author <a href="mailto:oliver.heger@t-online.de">Oliver Heger</a>
0159:         * @author <a href="mailto:ebourg@apache.org">Emmanuel Bourg</a>
0160:         * @version $Id: PropertiesConfiguration.java 439648 2006-09-02 20:42:10Z oheger $
0161:         */
0162:        public class PropertiesConfiguration extends AbstractFileConfiguration {
0163:            /** Constant for the supported comment characters.*/
0164:            static final String COMMENT_CHARS = "#!";
0165:
0166:            /**
0167:             * This is the name of the property that can point to other
0168:             * properties file for including other properties files.
0169:             */
0170:            private static String include = "include";
0171:
0172:            /** The list of possible key/value separators */
0173:            private static final char[] SEPARATORS = new char[] { '=', ':' };
0174:
0175:            /** The white space characters used as key/value separators. */
0176:            private static final char[] WHITE_SPACE = new char[] { ' ', '\t',
0177:                    '\f' };
0178:
0179:            /**
0180:             * The default encoding (ISO-8859-1 as specified by
0181:             * http://java.sun.com/j2se/1.5.0/docs/api/java/util/Properties.html)
0182:             */
0183:            private static final String DEFAULT_ENCODING = "ISO-8859-1";
0184:
0185:            /** Constant for the platform specific line separator.*/
0186:            private static final String LINE_SEPARATOR = System
0187:                    .getProperty("line.separator");
0188:
0189:            /** Constant for the radix of hex numbers.*/
0190:            private static final int HEX_RADIX = 16;
0191:
0192:            /** Constant for the length of a unicode literal.*/
0193:            private static final int UNICODE_LEN = 4;
0194:
0195:            /** Stores the layout object.*/
0196:            private PropertiesConfigurationLayout layout;
0197:
0198:            /** Allow file inclusion or not */
0199:            private boolean includesAllowed;
0200:
0201:            // initialization block to set the encoding before loading the file in the constructors
0202:            {
0203:                setEncoding(DEFAULT_ENCODING);
0204:            }
0205:
0206:            /**
0207:             * Creates an empty PropertyConfiguration object which can be
0208:             * used to synthesize a new Properties file by adding values and
0209:             * then saving().
0210:             */
0211:            public PropertiesConfiguration() {
0212:                layout = createLayout();
0213:                setIncludesAllowed(false);
0214:            }
0215:
0216:            /**
0217:             * Creates and loads the extended properties from the specified file.
0218:             * The specified file can contain "include = " properties which then
0219:             * are loaded and merged into the properties.
0220:             *
0221:             * @param fileName The name of the properties file to load.
0222:             * @throws ConfigurationException Error while loading the properties file
0223:             */
0224:            public PropertiesConfiguration(String fileName)
0225:                    throws ConfigurationException {
0226:                super (fileName);
0227:            }
0228:
0229:            /**
0230:             * Creates and loads the extended properties from the specified file.
0231:             * The specified file can contain "include = " properties which then
0232:             * are loaded and merged into the properties.
0233:             *
0234:             * @param file The properties file to load.
0235:             * @throws ConfigurationException Error while loading the properties file
0236:             */
0237:            public PropertiesConfiguration(File file)
0238:                    throws ConfigurationException {
0239:                super (file);
0240:            }
0241:
0242:            /**
0243:             * Creates and loads the extended properties from the specified URL.
0244:             * The specified file can contain "include = " properties which then
0245:             * are loaded and merged into the properties.
0246:             *
0247:             * @param url The location of the properties file to load.
0248:             * @throws ConfigurationException Error while loading the properties file
0249:             */
0250:            public PropertiesConfiguration(URL url)
0251:                    throws ConfigurationException {
0252:                super (url);
0253:            }
0254:
0255:            /**
0256:             * Gets the property value for including other properties files.
0257:             * By default it is "include".
0258:             *
0259:             * @return A String.
0260:             */
0261:            public static String getInclude() {
0262:                return PropertiesConfiguration.include;
0263:            }
0264:
0265:            /**
0266:             * Sets the property value for including other properties files.
0267:             * By default it is "include".
0268:             *
0269:             * @param inc A String.
0270:             */
0271:            public static void setInclude(String inc) {
0272:                PropertiesConfiguration.include = inc;
0273:            }
0274:
0275:            /**
0276:             * Controls whether additional files can be loaded by the include = <xxx>
0277:             * statement or not. Base rule is, that objects created by the empty
0278:             * C'tor can not have included files.
0279:             *
0280:             * @param includesAllowed includesAllowed True if Includes are allowed.
0281:             */
0282:            protected void setIncludesAllowed(boolean includesAllowed) {
0283:                this .includesAllowed = includesAllowed;
0284:            }
0285:
0286:            /**
0287:             * Reports the status of file inclusion.
0288:             *
0289:             * @return True if include files are loaded.
0290:             */
0291:            public boolean getIncludesAllowed() {
0292:                return this .includesAllowed;
0293:            }
0294:
0295:            /**
0296:             * Return the comment header.
0297:             *
0298:             * @return the comment header
0299:             * @since 1.1
0300:             */
0301:            public String getHeader() {
0302:                return getLayout().getHeaderComment();
0303:            }
0304:
0305:            /**
0306:             * Set the comment header.
0307:             *
0308:             * @param header the header to use
0309:             * @since 1.1
0310:             */
0311:            public void setHeader(String header) {
0312:                getLayout().setHeaderComment(header);
0313:            }
0314:
0315:            /**
0316:             * Returns the associated layout object.
0317:             *
0318:             * @return the associated layout object
0319:             * @since 1.3
0320:             */
0321:            public synchronized PropertiesConfigurationLayout getLayout() {
0322:                if (layout == null) {
0323:                    layout = createLayout();
0324:                }
0325:                return layout;
0326:            }
0327:
0328:            /**
0329:             * Sets the associated layout object.
0330:             *
0331:             * @param layout the new layout object; can be <b>null</b>, then a new
0332:             * layout object will be created
0333:             * @since 1.3
0334:             */
0335:            public synchronized void setLayout(
0336:                    PropertiesConfigurationLayout layout) {
0337:                // only one layout must exist
0338:                if (this .layout != null) {
0339:                    removeConfigurationListener(this .layout);
0340:                }
0341:
0342:                if (layout == null) {
0343:                    this .layout = createLayout();
0344:                } else {
0345:                    this .layout = layout;
0346:                }
0347:            }
0348:
0349:            /**
0350:             * Creates the associated layout object. This method is invoked when the
0351:             * layout object is accessed and has not been created yet. Derived classes
0352:             * can override this method to hook in a different layout implementation.
0353:             *
0354:             * @return the layout object to use
0355:             * @since 1.3
0356:             */
0357:            protected PropertiesConfigurationLayout createLayout() {
0358:                return new PropertiesConfigurationLayout(this );
0359:            }
0360:
0361:            /**
0362:             * Load the properties from the given reader.
0363:             * Note that the <code>clear()</code> method is not called, so
0364:             * the properties contained in the loaded file will be added to the
0365:             * actual set of properties.
0366:             *
0367:             * @param in An InputStream.
0368:             *
0369:             * @throws ConfigurationException if an error occurs
0370:             */
0371:            public synchronized void load(Reader in)
0372:                    throws ConfigurationException {
0373:                boolean oldAutoSave = isAutoSave();
0374:                setAutoSave(false);
0375:
0376:                try {
0377:                    getLayout().load(in);
0378:                } finally {
0379:                    setAutoSave(oldAutoSave);
0380:                }
0381:            }
0382:
0383:            /**
0384:             * Save the configuration to the specified stream.
0385:             *
0386:             * @param writer the output stream used to save the configuration
0387:             * @throws ConfigurationException if an error occurs
0388:             */
0389:            public void save(Writer writer) throws ConfigurationException {
0390:                enterNoReload();
0391:                try {
0392:                    getLayout().save(writer);
0393:                } finally {
0394:                    exitNoReload();
0395:                }
0396:            }
0397:
0398:            /**
0399:             * Extend the setBasePath method to turn includes
0400:             * on and off based on the existence of a base path.
0401:             *
0402:             * @param basePath The new basePath to set.
0403:             */
0404:            public void setBasePath(String basePath) {
0405:                super .setBasePath(basePath);
0406:                setIncludesAllowed(StringUtils.isNotEmpty(basePath));
0407:            }
0408:
0409:            /**
0410:             * Creates a copy of this object.
0411:             *
0412:             * @return the copy
0413:             */
0414:            public Object clone() {
0415:                PropertiesConfiguration copy = (PropertiesConfiguration) super 
0416:                        .clone();
0417:                if (layout != null) {
0418:                    copy.setLayout(new PropertiesConfigurationLayout(copy,
0419:                            layout));
0420:                }
0421:                return copy;
0422:            }
0423:
0424:            /**
0425:             * This method is invoked by the associated
0426:             * <code>{@link PropertiesConfigurationLayout}</code> object for each
0427:             * property definition detected in the parsed properties file. Its task is
0428:             * to check whether this is a special property definition (e.g. the
0429:             * <code>include</code> property). If not, the property must be added to
0430:             * this configuration. The return value indicates whether the property
0431:             * should be treated as a normal property. If it is <b>false</b>, the
0432:             * layout object will ignore this property.
0433:             *
0434:             * @param key the property key
0435:             * @param value the property value
0436:             * @return a flag whether this is a normal property
0437:             * @throws ConfigurationException if an error occurs
0438:             * @since 1.3
0439:             */
0440:            boolean propertyLoaded(String key, String value)
0441:                    throws ConfigurationException {
0442:                boolean result;
0443:
0444:                if (StringUtils.isNotEmpty(getInclude())
0445:                        && key.equalsIgnoreCase(getInclude())) {
0446:                    if (getIncludesAllowed()) {
0447:                        String[] files;
0448:                        if (!isDelimiterParsingDisabled()) {
0449:                            files = StringUtils
0450:                                    .split(value, getListDelimiter());
0451:                        } else {
0452:                            files = new String[] { value };
0453:                        }
0454:                        for (int i = 0; i < files.length; i++) {
0455:                            loadIncludeFile(files[i].trim());
0456:                        }
0457:                    }
0458:                    result = false;
0459:                }
0460:
0461:                else {
0462:                    addProperty(key, value);
0463:                    result = true;
0464:                }
0465:
0466:                return result;
0467:            }
0468:
0469:            /**
0470:             * Tests whether a line is a comment, i.e. whether it starts with a comment
0471:             * character.
0472:             *
0473:             * @param line the line
0474:             * @return a flag if this is a comment line
0475:             * @since 1.3
0476:             */
0477:            static boolean isCommentLine(String line) {
0478:                String s = line.trim();
0479:                // blanc lines are also treated as comment lines
0480:                return s.length() < 1
0481:                        || COMMENT_CHARS.indexOf(s.charAt(0)) >= 0;
0482:            }
0483:
0484:            /**
0485:             * This class is used to read properties lines. These lines do
0486:             * not terminate with new-line chars but rather when there is no
0487:             * backslash sign a the end of the line.  This is used to
0488:             * concatenate multiple lines for readability.
0489:             */
0490:            public static class PropertiesReader extends LineNumberReader {
0491:                /** Stores the comment lines for the currently processed property.*/
0492:                private List commentLines;
0493:
0494:                /** Stores the name of the last read property.*/
0495:                private String propertyName;
0496:
0497:                /** Stores the value of the last read property.*/
0498:                private String propertyValue;
0499:
0500:                /** Stores the list delimiter character.*/
0501:                private char delimiter;
0502:
0503:                /**
0504:                 * Constructor.
0505:                 *
0506:                 * @param reader A Reader.
0507:                 */
0508:                public PropertiesReader(Reader reader) {
0509:                    this (reader, AbstractConfiguration
0510:                            .getDefaultListDelimiter());
0511:                }
0512:
0513:                /**
0514:                 * Creates a new instance of <code>PropertiesReader</code> and sets
0515:                 * the underlaying reader and the list delimiter.
0516:                 *
0517:                 * @param reader the reader
0518:                 * @param listDelimiter the list delimiter character
0519:                 * @since 1.3
0520:                 */
0521:                public PropertiesReader(Reader reader, char listDelimiter) {
0522:                    super (reader);
0523:                    commentLines = new ArrayList();
0524:                    delimiter = listDelimiter;
0525:                }
0526:
0527:                /**
0528:                 * Reads a property line. Returns null if Stream is
0529:                 * at EOF. Concatenates lines ending with "\".
0530:                 * Skips lines beginning with "#" or "!" and empty lines.
0531:                 * The return value is a property definition (<code>&lt;name&gt;</code>
0532:                 * = <code>&lt;value&gt;</code>)
0533:                 *
0534:                 * @return A string containing a property value or null
0535:                 *
0536:                 * @throws IOException in case of an I/O error
0537:                 */
0538:                public String readProperty() throws IOException {
0539:                    commentLines.clear();
0540:                    StringBuffer buffer = new StringBuffer();
0541:
0542:                    while (true) {
0543:                        String line = readLine();
0544:                        if (line == null) {
0545:                            // EOF
0546:                            return null;
0547:                        }
0548:
0549:                        if (isCommentLine(line)) {
0550:                            commentLines.add(line);
0551:                            continue;
0552:                        }
0553:
0554:                        line = line.trim();
0555:
0556:                        if (checkCombineLines(line)) {
0557:                            line = line.substring(0, line.length() - 1);
0558:                            buffer.append(line);
0559:                        } else {
0560:                            buffer.append(line);
0561:                            break;
0562:                        }
0563:                    }
0564:                    return buffer.toString();
0565:                }
0566:
0567:                /**
0568:                 * Parses the next property from the input stream and stores the found
0569:                 * name and value in internal fields. These fields can be obtained using
0570:                 * the provided getter methods. The return value indicates whether EOF
0571:                 * was reached (<b>false</b>) or whether further properties are
0572:                 * available (<b>true</b>).
0573:                 *
0574:                 * @return a flag if further properties are available
0575:                 * @throws IOException if an error occurs
0576:                 * @since 1.3
0577:                 */
0578:                public boolean nextProperty() throws IOException {
0579:                    String line = readProperty();
0580:
0581:                    if (line == null) {
0582:                        return false; // EOF
0583:                    }
0584:
0585:                    // parse the line
0586:                    String[] property = parseProperty(line);
0587:                    propertyName = StringEscapeUtils.unescapeJava(property[0]);
0588:                    propertyValue = unescapeJava(property[1], delimiter);
0589:                    return true;
0590:                }
0591:
0592:                /**
0593:                 * Returns the comment lines that have been read for the last property.
0594:                 *
0595:                 * @return the comment lines for the last property returned by
0596:                 * <code>readProperty()</code>
0597:                 * @since 1.3
0598:                 */
0599:                public List getCommentLines() {
0600:                    return commentLines;
0601:                }
0602:
0603:                /**
0604:                 * Returns the name of the last read property. This method can be called
0605:                 * after <code>{@link #nextProperty()}</code> was invoked and its
0606:                 * return value was <b>true</b>.
0607:                 *
0608:                 * @return the name of the last read property
0609:                 * @since 1.3
0610:                 */
0611:                public String getPropertyName() {
0612:                    return propertyName;
0613:                }
0614:
0615:                /**
0616:                 * Returns the value of the last read property. This method can be
0617:                 * called after <code>{@link #nextProperty()}</code> was invoked and
0618:                 * its return value was <b>true</b>.
0619:                 *
0620:                 * @return the value of the last read property
0621:                 * @since 1.3
0622:                 */
0623:                public String getPropertyValue() {
0624:                    return propertyValue;
0625:                }
0626:
0627:                /**
0628:                 * Checks if the passed in line should be combined with the following.
0629:                 * This is true, if the line ends with an odd number of backslashes.
0630:                 *
0631:                 * @param line the line
0632:                 * @return a flag if the lines should be combined
0633:                 */
0634:                private static boolean checkCombineLines(String line) {
0635:                    int bsCount = 0;
0636:                    for (int idx = line.length() - 1; idx >= 0
0637:                            && line.charAt(idx) == '\\'; idx--) {
0638:                        bsCount++;
0639:                    }
0640:
0641:                    return bsCount % 2 == 1;
0642:                }
0643:
0644:                /**
0645:                 * Parse a property line and return the key and the value in an array.
0646:                 *
0647:                 * @param line the line to parse
0648:                 * @return an array with the property's key and value
0649:                 * @since 1.2
0650:                 */
0651:                private static String[] parseProperty(String line) {
0652:                    // sorry for this spaghetti code, please replace it as soon as
0653:                    // possible with a regexp when the Java 1.3 requirement is dropped
0654:
0655:                    String[] result = new String[2];
0656:                    StringBuffer key = new StringBuffer();
0657:                    StringBuffer value = new StringBuffer();
0658:
0659:                    // state of the automaton:
0660:                    // 0: key parsing
0661:                    // 1: antislash found while parsing the key
0662:                    // 2: separator crossing
0663:                    // 3: value parsing
0664:                    int state = 0;
0665:
0666:                    for (int pos = 0; pos < line.length(); pos++) {
0667:                        char c = line.charAt(pos);
0668:
0669:                        switch (state) {
0670:                        case 0:
0671:                            if (c == '\\') {
0672:                                state = 1;
0673:                            } else if (ArrayUtils.contains(WHITE_SPACE, c)) {
0674:                                // switch to the separator crossing state
0675:                                state = 2;
0676:                            } else if (ArrayUtils.contains(SEPARATORS, c)) {
0677:                                // switch to the value parsing state
0678:                                state = 3;
0679:                            } else {
0680:                                key.append(c);
0681:                            }
0682:
0683:                            break;
0684:
0685:                        case 1:
0686:                            if (ArrayUtils.contains(SEPARATORS, c)
0687:                                    || ArrayUtils.contains(WHITE_SPACE, c)) {
0688:                                // this is an escaped separator or white space
0689:                                key.append(c);
0690:                            } else {
0691:                                // another escaped character, the '\' is preserved
0692:                                key.append('\\');
0693:                                key.append(c);
0694:                            }
0695:
0696:                            // return to the key parsing state
0697:                            state = 0;
0698:
0699:                            break;
0700:
0701:                        case 2:
0702:                            if (ArrayUtils.contains(WHITE_SPACE, c)) {
0703:                                // do nothing, eat all white spaces
0704:                                state = 2;
0705:                            } else if (ArrayUtils.contains(SEPARATORS, c)) {
0706:                                // switch to the value parsing state
0707:                                state = 3;
0708:                            } else {
0709:                                // any other character indicates we encoutered the beginning of the value
0710:                                value.append(c);
0711:
0712:                                // switch to the value parsing state
0713:                                state = 3;
0714:                            }
0715:
0716:                            break;
0717:
0718:                        case 3:
0719:                            value.append(c);
0720:                            break;
0721:                        }
0722:                    }
0723:
0724:                    result[0] = key.toString().trim();
0725:                    result[1] = value.toString().trim();
0726:
0727:                    return result;
0728:                }
0729:            } // class PropertiesReader
0730:
0731:            /**
0732:             * This class is used to write properties lines.
0733:             */
0734:            public static class PropertiesWriter extends FilterWriter {
0735:                /** The delimiter for multi-valued properties.*/
0736:                private char delimiter;
0737:
0738:                /**
0739:                 * Constructor.
0740:                 *
0741:                 * @param writer a Writer object providing the underlying stream
0742:                 * @param delimiter the delimiter character for multi-valued properties
0743:                 */
0744:                public PropertiesWriter(Writer writer, char delimiter) {
0745:                    super (writer);
0746:                    this .delimiter = delimiter;
0747:                }
0748:
0749:                /**
0750:                 * Write a property.
0751:                 *
0752:                 * @param key the key of the property
0753:                 * @param value the value of the property
0754:                 *
0755:                 * @throws IOException if an I/O error occurs
0756:                 */
0757:                public void writeProperty(String key, Object value)
0758:                        throws IOException {
0759:                    writeProperty(key, value, false);
0760:                }
0761:
0762:                /**
0763:                 * Write a property.
0764:                 *
0765:                 * @param key The key of the property
0766:                 * @param values The array of values of the property
0767:                 *
0768:                 * @throws IOException if an I/O error occurs
0769:                 */
0770:                public void writeProperty(String key, List values)
0771:                        throws IOException {
0772:                    for (int i = 0; i < values.size(); i++) {
0773:                        writeProperty(key, values.get(i));
0774:                    }
0775:                }
0776:
0777:                /**
0778:                 * Writes the given property and its value. If the value happens to be a
0779:                 * list, the <code>forceSingleLine</code> flag is evaluated. If it is
0780:                 * set, all values are written on a single line using the list delimiter
0781:                 * as separator.
0782:                 *
0783:                 * @param key the property key
0784:                 * @param value the property value
0785:                 * @param forceSingleLine the &quot;force single line&quot; flag
0786:                 * @throws IOException if an error occurs
0787:                 * @since 1.3
0788:                 */
0789:                public void writeProperty(String key, Object value,
0790:                        boolean forceSingleLine) throws IOException {
0791:                    String v;
0792:
0793:                    if (value instanceof  List) {
0794:                        List values = (List) value;
0795:                        if (forceSingleLine) {
0796:                            v = makeSingleLineValue(values);
0797:                        } else {
0798:                            writeProperty(key, values);
0799:                            return;
0800:                        }
0801:                    } else {
0802:                        v = escapeValue(value);
0803:                    }
0804:
0805:                    write(escapeKey(key));
0806:                    write(" = ");
0807:                    write(v);
0808:
0809:                    writeln(null);
0810:                }
0811:
0812:                /**
0813:                 * Write a comment.
0814:                 *
0815:                 * @param comment the comment to write
0816:                 * @throws IOException if an I/O error occurs
0817:                 */
0818:                public void writeComment(String comment) throws IOException {
0819:                    writeln("# " + comment);
0820:                }
0821:
0822:                /**
0823:                 * Escape the separators in the key.
0824:                 *
0825:                 * @param key the key
0826:                 * @return the escaped key
0827:                 * @since 1.2
0828:                 */
0829:                private String escapeKey(String key) {
0830:                    StringBuffer newkey = new StringBuffer();
0831:
0832:                    for (int i = 0; i < key.length(); i++) {
0833:                        char c = key.charAt(i);
0834:
0835:                        if (ArrayUtils.contains(SEPARATORS, c)
0836:                                || ArrayUtils.contains(WHITE_SPACE, c)) {
0837:                            // escape the separator
0838:                            newkey.append('\\');
0839:                            newkey.append(c);
0840:                        } else {
0841:                            newkey.append(c);
0842:                        }
0843:                    }
0844:
0845:                    return newkey.toString();
0846:                }
0847:
0848:                /**
0849:                 * Escapes the given property value. Delimiter characters in the value
0850:                 * will be escaped.
0851:                 *
0852:                 * @param value the property value
0853:                 * @return the escaped property value
0854:                 * @since 1.3
0855:                 */
0856:                private String escapeValue(Object value) {
0857:                    String v = StringEscapeUtils.escapeJava(String
0858:                            .valueOf(value));
0859:                    return StringUtils.replace(v, String.valueOf(delimiter),
0860:                            "\\" + delimiter);
0861:                }
0862:
0863:                /**
0864:                 * Transforms a list of values into a single line value.
0865:                 *
0866:                 * @param values the list with the values
0867:                 * @return a string with the single line value (can be <b>null</b>)
0868:                 * @since 1.3
0869:                 */
0870:                private String makeSingleLineValue(List values) {
0871:                    if (!values.isEmpty()) {
0872:                        Iterator it = values.iterator();
0873:                        StringBuffer buf = new StringBuffer(escapeValue(it
0874:                                .next()));
0875:                        while (it.hasNext()) {
0876:                            buf.append(delimiter);
0877:                            buf.append(escapeValue(it.next()));
0878:                        }
0879:                        return buf.toString();
0880:                    } else {
0881:                        return null;
0882:                    }
0883:                }
0884:
0885:                /**
0886:                 * Helper method for writing a line with the platform specific line
0887:                 * ending.
0888:                 *
0889:                 * @param s the content of the line (may be <b>null</b>)
0890:                 * @throws IOException if an error occurs
0891:                 * @since 1.3
0892:                 */
0893:                public void writeln(String s) throws IOException {
0894:                    if (s != null) {
0895:                        write(s);
0896:                    }
0897:                    write(LINE_SEPARATOR);
0898:                }
0899:
0900:            } // class PropertiesWriter
0901:
0902:            /**
0903:             * <p>Unescapes any Java literals found in the <code>String</code> to a
0904:             * <code>Writer</code>.</p> This is a slightly modified version of the
0905:             * StringEscapeUtils.unescapeJava() function in commons-lang that doesn't
0906:             * drop escaped separators (i.e '\,').
0907:             *
0908:             * @param str  the <code>String</code> to unescape, may be null
0909:             * @param delimiter the delimiter for multi-valued properties
0910:             * @return the processed string
0911:             * @throws IllegalArgumentException if the Writer is <code>null</code>
0912:             */
0913:            protected static String unescapeJava(String str, char delimiter) {
0914:                if (str == null) {
0915:                    return null;
0916:                }
0917:                int sz = str.length();
0918:                StringBuffer out = new StringBuffer(sz);
0919:                StringBuffer unicode = new StringBuffer(UNICODE_LEN);
0920:                boolean hadSlash = false;
0921:                boolean inUnicode = false;
0922:                for (int i = 0; i < sz; i++) {
0923:                    char ch = str.charAt(i);
0924:                    if (inUnicode) {
0925:                        // if in unicode, then we're reading unicode
0926:                        // values in somehow
0927:                        unicode.append(ch);
0928:                        if (unicode.length() == UNICODE_LEN) {
0929:                            // unicode now contains the four hex digits
0930:                            // which represents our unicode character
0931:                            try {
0932:                                int value = Integer.parseInt(
0933:                                        unicode.toString(), HEX_RADIX);
0934:                                out.append((char) value);
0935:                                unicode.setLength(0);
0936:                                inUnicode = false;
0937:                                hadSlash = false;
0938:                            } catch (NumberFormatException nfe) {
0939:                                throw new ConfigurationRuntimeException(
0940:                                        "Unable to parse unicode value: "
0941:                                                + unicode, nfe);
0942:                            }
0943:                        }
0944:                        continue;
0945:                    }
0946:
0947:                    if (hadSlash) {
0948:                        // handle an escaped value
0949:                        hadSlash = false;
0950:
0951:                        if (ch == '\\') {
0952:                            out.append('\\');
0953:                        } else if (ch == '\'') {
0954:                            out.append('\'');
0955:                        } else if (ch == '\"') {
0956:                            out.append('"');
0957:                        } else if (ch == 'r') {
0958:                            out.append('\r');
0959:                        } else if (ch == 'f') {
0960:                            out.append('\f');
0961:                        } else if (ch == 't') {
0962:                            out.append('\t');
0963:                        } else if (ch == 'n') {
0964:                            out.append('\n');
0965:                        } else if (ch == 'b') {
0966:                            out.append('\b');
0967:                        } else if (ch == delimiter) {
0968:                            out.append('\\');
0969:                            out.append(delimiter);
0970:                        } else if (ch == 'u') {
0971:                            // uh-oh, we're in unicode country....
0972:                            inUnicode = true;
0973:                        } else {
0974:                            out.append(ch);
0975:                        }
0976:
0977:                        continue;
0978:                    } else if (ch == '\\') {
0979:                        hadSlash = true;
0980:                        continue;
0981:                    }
0982:                    out.append(ch);
0983:                }
0984:
0985:                if (hadSlash) {
0986:                    // then we're in the weird case of a \ at the end of the
0987:                    // string, let's output it anyway.
0988:                    out.append('\\');
0989:                }
0990:
0991:                return out.toString();
0992:            }
0993:
0994:            /**
0995:             * Helper method for loading an included properties file. This method is
0996:             * called by <code>load()</code> when an <code>include</code> property
0997:             * is encountered. It tries to resolve relative file names based on the
0998:             * current base path. If this fails, a resolution based on the location of
0999:             * this properties file is tried.
1000:             *
1001:             * @param fileName the name of the file to load
1002:             * @throws ConfigurationException if loading fails
1003:             */
1004:            private void loadIncludeFile(String fileName)
1005:                    throws ConfigurationException {
1006:                URL url = ConfigurationUtils.locate(getBasePath(), fileName);
1007:                if (url == null) {
1008:                    URL baseURL = getURL();
1009:                    if (baseURL != null) {
1010:                        url = ConfigurationUtils.locate(baseURL.toString(),
1011:                                fileName);
1012:                    }
1013:                }
1014:
1015:                if (url == null) {
1016:                    throw new ConfigurationException(
1017:                            "Cannot resolve include file " + fileName);
1018:                }
1019:                load(url);
1020:            }
1021:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.