001/*
002    Licensed to the Apache Software Foundation (ASF) under one
003    or more contributor license agreements.  See the NOTICE file
004    distributed with this work for additional information
005    regarding copyright ownership.  The ASF licenses this file
006    to you under the Apache License, Version 2.0 (the
007    "License"); you may not use this file except in compliance
008    with the License.  You may obtain a copy of the License at
009
010       http://www.apache.org/licenses/LICENSE-2.0
011
012    Unless required by applicable law or agreed to in writing,
013    software distributed under the License is distributed on an
014    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015    KIND, either express or implied.  See the License for the
016    specific language governing permissions and limitations
017    under the License.  
018 */
019package org.apache.wiki.util;
020
021import java.util.Iterator;
022import java.util.Map;
023import java.util.Properties;
024import java.util.TreeMap;
025
026/**
027 * some useful methods for properties
028 *
029 * @version 1.0
030 */
031public final class PropertiesUtils {
032
033    private static final String OTHER_WHITESPACE = "\t\r\n\014";
034    private static final char[] HEXDIGIT = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
035
036    /** Private constructor to prevent instantiation. */
037    private PropertiesUtils()
038    {}
039
040    /**
041     * <p>
042     * like Properties.store, but stores the properties in sorted order
043     * </p>
044     *
045     * @param properties the properties object
046     * @return String the properties, nicely formatted 
047     */
048    public static String toSortedString(final Properties properties )
049    {
050        @SuppressWarnings( { "unchecked", "rawtypes" } ) final TreeMap< String, String > treemap = new TreeMap( properties );
051        final StringBuilder string = new StringBuilder();
052        final Iterator< Map.Entry< String, String > > iterator = treemap.entrySet().iterator();
053        while( iterator.hasNext() )
054        {
055            final Map.Entry< String, String > entry = iterator.next();
056            final String key = entry.getKey();
057            final String value = entry.getValue() == null ? "null" : entry.getValue();
058            string.append(toLine(key, value)).append('\n');
059        }
060        return string.toString();
061    }
062
063    /**
064     * Generates a property file line from a supplied key and value.
065     * @param key the property's key
066     * @param value the property's value
067     * @return the converted string
068     */
069    public static String toLine(final String key, final String value )
070    {
071        return saveConvert( key, true ) + '=' + saveConvert( value, false );
072    }
073
074    /**
075     * Encodes a property file string from a supplied key/value line.
076     * @param string the string to encode
077     * @param encodeWhiteSpace <code>true</code> if whitespace should be encoded also
078     * @return the converted string
079     */
080    public static String saveConvert(final String string, final boolean encodeWhiteSpace )
081    {
082        final int i = string.length();
083        final StringBuilder stringbuffer = new StringBuilder( i * 2 );
084        for( int i3 = 0; i3 < i; i3++ )
085        {
086            final char c = string.charAt( i3 );
087            switch( c )
088            {
089                case ' ':
090                    if( i3 == 0 || encodeWhiteSpace )
091                    {
092                        stringbuffer.append( '\\' );
093                    }
094                    stringbuffer.append( ' ' );
095                    break;
096                case '\\':
097                    stringbuffer.append( '\\' );
098                    stringbuffer.append( '\\' );
099                    break;
100                case '\t':
101                    stringbuffer.append( '\\' );
102                    stringbuffer.append( 't' );
103                    break;
104                case '\n':
105                    stringbuffer.append( '\\' );
106                    stringbuffer.append( 'n' );
107                    break;
108                case '\r':
109                    stringbuffer.append( '\\' );
110                    stringbuffer.append( 'r' );
111                    break;
112                case '\014':
113                    stringbuffer.append( '\\' );
114                    stringbuffer.append( 'f' );
115                    break;
116                default:
117                    if( c < 32 || c > 126 )
118                    {
119                        stringbuffer.append( '\\' );
120                        stringbuffer.append( 'u' );
121                        stringbuffer.append( toHex( c >> 12 & 0xf ) );
122                        stringbuffer.append( toHex( c >> 8 & 0xf ) );
123                        stringbuffer.append( toHex( c >> 4 & 0xf ) );
124                        stringbuffer.append( toHex( c & 0xf ) );
125                    }
126                    else
127                    {
128                        if( OTHER_WHITESPACE.indexOf( c ) != -1 )
129                        {
130                            stringbuffer.append( '\\' );
131                        }
132                        stringbuffer.append( c );
133                    }
134            }
135        }
136        return stringbuffer.toString();
137    }
138
139    private static char toHex(final int i )
140    {
141        return HEXDIGIT[i & 0xf];
142    }
143}