/* * JBoss, Home of Professional Open Source * Copyright 2010, Red Hat, Inc. and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.richfaces.convert; import java.text.MessageFormat; import javax.faces.component.UINamingContainer; import javax.faces.context.FacesContext; /** * @author Nick Belaevski * */ public final class TreeConverterUtil { private static final class SeparatorCharHolder { static final char SEPARATOR_CHAR = UINamingContainer.getSeparatorChar(FacesContext.getCurrentInstance()); private SeparatorCharHolder() { } } private static final char ESCAPE_CHAR = '_'; private TreeConverterUtil() { } private static boolean shouldEscape(char c) { if ('a' <= c && c <= 'z') { return false; } if ('A' <= c && c <= 'Z') { return false; } if ('0' <= c && c <= '9') { return false; } if (c == ESCAPE_CHAR || c == SeparatorCharHolder.SEPARATOR_CHAR) { return true; } if ('-' == c || '\u00B7' == c) { return false; } if ('\u00C0' <= c && c <= '\u00D6') { return false; } if ('\u00D8' <= c && c <= '\u00F6') { return false; } if ('\u00F8' <= c && c <= '\u02FF') { return false; } if ('\u0370' <= c && c <= '\u037D') { return false; } if ('\u037F' <= c && c <= '\u1FFF') { return false; } if ('\u200C' <= c && c <= '\u200D') { return false; } if ('\u2070' <= c && c <= '\u218F') { return false; } if ('\u2C00' <= c && c <= '\u2FEF') { return false; } if ('\u3001' <= c && c <= '\uD7FF') { return false; } if ('\uF900' <= c && c <= '\uFDCF') { return false; } if ('\uFDF0' <= c && c <= '\uFFFD') { return false; } if ('\u0300' <= c && c <= '\u036F') { return false; } if ('\u203F' <= c && c <= '\u2040') { return false; } return true; } private static int parseHexString(String s) { int result = 0; for (int i = 0; i < s.length(); i++) { result <<= 4; char c = s.charAt(i); int digitValue = Character.digit(c, 16); if (digitValue < 0) { throw new NumberFormatException(); } result += digitValue; } return result; } private static void checkAvailable(String s, int idx, int len) { if (s.length() < idx + len) { throw new IllegalArgumentException(MessageFormat.format( "Expected {0} available chars in ''{1}'' string starting from {2} index", len, s, idx)); } } private static char getNextChar(String s, int idx) { checkAvailable(s, idx, 1); return s.charAt(idx); } private static char readNextCharFromHexCode(String s, int idx, int len) { checkAvailable(s, idx, len); String numString = s.substring(idx, idx + len); try { int charValue = parseHexString(numString); return (char) charValue; } catch (NumberFormatException e) { throw new IllegalArgumentException(MessageFormat.format( "Substring ''{0}'' of ''{1}'' string is not a valid hex number ", numString, s)); } } public static String unescape(String s) { StringBuilder sb = new StringBuilder(s.length()); int startIdx = 0; while (startIdx >= 0) { int idx = s.indexOf(ESCAPE_CHAR, startIdx); if (idx >= 0) { sb.append(s.subSequence(startIdx, idx)); idx++; // skip escape char char c = getNextChar(s, idx); switch (c) { case ESCAPE_CHAR: idx++; sb.append(c); break; case 'x': idx++; sb.append(readNextCharFromHexCode(s, idx, 2)); idx += 2; break; case 'u': idx++; sb.append(readNextCharFromHexCode(s, idx, 4)); idx += 4; break; default: throw new IllegalArgumentException(MessageFormat.format( "Unexpected char ''{0}'' in ''{1}'' string located at index {2}", c, s, idx)); } } else { sb.append(s.subSequence(startIdx, s.length())); } startIdx = idx; } return sb.toString(); } public static String escape(String s) { StringBuilder sb = new StringBuilder(s.length()); int start = 0; char[] cs = s.toCharArray(); for (int idx = 0; idx < cs.length; idx++) { char c = cs[idx]; if (shouldEscape(c)) { sb.append(cs, start, idx - start); sb.append(ESCAPE_CHAR); if (c == ESCAPE_CHAR) { sb.append(c); // ${escapeChar} } else { String asHex = Integer.toHexString(c); switch (asHex.length()) { case 1: sb.append("x0"); // _x05 break; case 2: sb.append("x"); // _xef break; case 3: sb.append("u0"); // _u0fed break; case 4: sb.append("u"); // _ufcda break; default: throw new IllegalArgumentException(); } sb.append(asHex); } start = idx + 1; } } if (start < s.length()) { sb.append(cs, start, s.length() - start); } return sb.toString(); } }