001    package ezvcard.util;
002    
003    import java.util.ArrayList;
004    import java.util.List;
005    import java.util.regex.Pattern;
006    
007    /*
008     Copyright (c) 2012, Michael Angstadt
009     All rights reserved.
010    
011     Redistribution and use in source and binary forms, with or without
012     modification, are permitted provided that the following conditions are met: 
013    
014     1. Redistributions of source code must retain the above copyright notice, this
015     list of conditions and the following disclaimer. 
016     2. Redistributions in binary form must reproduce the above copyright notice,
017     this list of conditions and the following disclaimer in the documentation
018     and/or other materials provided with the distribution. 
019    
020     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
021     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
022     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
023     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
024     ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
025     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
026     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
027     ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
028     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
029     SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
030    
031     The views and conclusions contained in the software and documentation are those
032     of the authors and should not be interpreted as representing official policies, 
033     either expressed or implied, of the FreeBSD Project.
034     */
035    
036    /**
037     * Helper class for dealing with vCard strings.
038     * @author Michael Angstadt
039     */
040    public class VCardStringUtils {
041            /**
042             * Unescapes all special characters that are escaped with a backslash, as
043             * well as escaped newlines.
044             * @param text the text
045             * @return the unescaped text
046             */
047            public static String unescape(String text) {
048                    StringBuilder sb = new StringBuilder(text.length());
049                    boolean escaped = false;
050                    for (int i = 0; i < text.length(); i++) {
051                            char ch = text.charAt(i);
052                            if (escaped) {
053                                    if (ch == 'n' || ch == 'N') {
054                                            //newlines appear as "\n" or "\N" (see RFC 2426 p.7)
055                                            sb.append(System.getProperty("line.separator"));
056                                    } else {
057                                            sb.append(ch);
058                                    }
059                                    escaped = false;
060                            } else if (ch == '\\') {
061                                    escaped = true;
062                            } else {
063                                    sb.append(ch);
064                            }
065                    }
066                    return sb.toString();
067            }
068    
069            /**
070             * Escapes all special characters within a vCard value.
071             * <p>
072             * These characters are:
073             * </p>
074             * <ul>
075             * <li>backslashes (<code>\</code>)</li>
076             * <li>commas (<code>,</code>)</li>
077             * <li>semi-colons (<code>;</code>)</li>
078             * </ul>
079             * @param text the text to escape
080             * @return the escaped text
081             */
082            public static String escape(String text) {
083                    String chars = "\\,;";
084                    for (int i = 0; i < chars.length(); i++) {
085                            String ch = chars.substring(i, i + 1);
086                            text = text.replace(ch, "\\" + ch);
087                    }
088                    return text;
089            }
090    
091            /**
092             * Escapes all newline characters within a vCard value.
093             * <p>
094             * This method escapes the following newline sequences:
095             * </p>
096             * <ul>
097             * <li><code>\r\n</code></li>
098             * <li><code>\r</code></li>
099             * <li><code>\n</code></li>
100             * </ul>
101             * @param text the text to escape
102             * @return the escaped text
103             */
104            public static String escapeNewlines(String text) {
105                    return text.replaceAll("\\r\\n|\\r|\\n", "\\\\n");
106            }
107    
108            /**
109             * Determines if a string contains one or more newline characters.
110             * @param text the string
111             * @return true if it contains one or more newline characters, false if not
112             */
113            public static boolean containsNewlines(String text) {
114                    return text.contains("\n") || text.contains("\r");
115            }
116    
117            /**
118             * Splits a string by a character, taking escaped characters into account.
119             * Each split value is also trimmed.
120             * <p>
121             * For example:
122             * <p>
123             * <code>splitBy("HE\:LLO::WORLD", ':', false, true)</code>
124             * <p>
125             * returns
126             * <p>
127             * <code>["HE:LLO", "", "WORLD"]</code>
128             * @param str the string to split
129             * @param ch the character to split by
130             * @param removeEmpties true to remove empty elements, false not to
131             * @param unescape true to unescape each split string, false not to
132             * @return the split string
133             * @see <a
134             * href="http://stackoverflow.com/q/820172">http://stackoverflow.com/q/820172</a>
135             */
136            public static String[] splitBy(String str, char ch, boolean removeEmpties, boolean unescape) {
137                    str = str.trim();
138                    String split[] = str.split("\\s*(?<!\\\\)" + Pattern.quote(ch + "") + "\\s*", -1);
139    
140                    List<String> list = new ArrayList<String>(split.length);
141                    for (String s : split) {
142                            if (s.length() == 0 && removeEmpties) {
143                                    continue;
144                            }
145    
146                            if (unescape) {
147                                    s = unescape(s);
148                            }
149    
150                            list.add(s);
151                    }
152    
153                    return list.toArray(new String[0]);
154            }
155    
156            /**
157             * Trims the whitespace off the left side of a string.
158             * @param string the string to trim
159             * @return the trimmed string
160             */
161            public static String ltrim(String string) {
162                    int i;
163                    for (i = 0; i < string.length() && Character.isWhitespace(string.charAt(i)); i++) {
164                            //do nothing
165                    }
166                    return (i == string.length()) ? "" : string.substring(i);
167            }
168    
169            /**
170             * Trims the whitespace off the right side of a string.
171             * @param string the string to trim
172             * @return the trimmed string
173             */
174            public static String rtrim(String string) {
175                    int i;
176                    for (i = string.length() - 1; i >= 0 && Character.isWhitespace(string.charAt(i)); i--) {
177                            //do nothing
178                    }
179                    return (i == 0) ? "" : string.substring(0, i + 1);
180            }
181    
182            private VCardStringUtils() {
183                    //hide constructor
184            }
185    }