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( Properties properties )
049    {
050        @SuppressWarnings( { "unchecked", "rawtypes" } )
051        TreeMap< String, String > treemap = new TreeMap( properties );
052        String string = "";
053        Iterator< Map.Entry< String, String > > iterator = treemap.entrySet().iterator();
054        while( iterator.hasNext() )
055        {
056            Map.Entry< String, String > entry = iterator.next();
057            String key = entry.getKey();
058            String value = entry.getValue() == null ? "null" : entry.getValue();
059            string += toLine( key, value ) + '\n';
060        }
061        return string;
062    }
063
064    /**
065     * Generates a property file line from a supplied key and value.
066     * @param key the property's key
067     * @param value the property's value
068     * @return the converted string
069     */
070    public static String toLine( String key, String value )
071    {
072        return saveConvert( key, true ) + '=' + saveConvert( value, false );
073    }
074
075    /**
076     * Encodes a property file string from a supplied key/value line.
077     * @param string the string to encode
078     * @param encodeWhiteSpace <code>true</code> if whitespace should be encoded also
079     * @return the converted string
080     */
081    public static String saveConvert( String string, boolean encodeWhiteSpace )
082    {
083        int i = string.length();
084        StringBuilder stringbuffer = new StringBuilder( i * 2 );
085        for( int i3 = 0; i3 < i; i3++ )
086        {
087            char c = string.charAt( i3 );
088            switch( c )
089            {
090                case ' ':
091                    if( i3 == 0 || encodeWhiteSpace )
092                    {
093                        stringbuffer.append( '\\' );
094                    }
095                    stringbuffer.append( ' ' );
096                    break;
097                case '\\':
098                    stringbuffer.append( '\\' );
099                    stringbuffer.append( '\\' );
100                    break;
101                case '\t':
102                    stringbuffer.append( '\\' );
103                    stringbuffer.append( 't' );
104                    break;
105                case '\n':
106                    stringbuffer.append( '\\' );
107                    stringbuffer.append( 'n' );
108                    break;
109                case '\r':
110                    stringbuffer.append( '\\' );
111                    stringbuffer.append( 'r' );
112                    break;
113                case '\014':
114                    stringbuffer.append( '\\' );
115                    stringbuffer.append( 'f' );
116                    break;
117                default:
118                    if( c < 32 || c > 126 )
119                    {
120                        stringbuffer.append( '\\' );
121                        stringbuffer.append( 'u' );
122                        stringbuffer.append( toHex( c >> 12 & 0xf ) );
123                        stringbuffer.append( toHex( c >> 8 & 0xf ) );
124                        stringbuffer.append( toHex( c >> 4 & 0xf ) );
125                        stringbuffer.append( toHex( c & 0xf ) );
126                    }
127                    else
128                    {
129                        if( OTHER_WHITESPACE.indexOf( c ) != -1 )
130                        {
131                            stringbuffer.append( '\\' );
132                        }
133                        stringbuffer.append( c );
134                    }
135            }
136        }
137        return stringbuffer.toString();
138    }
139
140    private static char toHex( int i )
141    {
142        return HEXDIGIT[i & 0xf];
143    }
144}