001/*
002 * ModeShape (http://www.modeshape.org)
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *       http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.modeshape.common.text;
017
018import java.text.CharacterIterator;
019import java.text.StringCharacterIterator;
020import org.modeshape.common.annotation.Immutable;
021
022/**
023 * Encoder that escapes characters that are not allowed in JCR names. The mapping defined in Section 3.6.3 of the JSR-283 public
024 * review document:
025 * <table cellspacing="0" cellpadding="1" border="1">
026 * <tr>
027 * <th>Non-JCR character<br/>
028 * (Unicode code point)</th>
029 * <th>Private use<br/>
030 * Unicode code point</th>
031 * </tr>
032 * <tr>
033 * <td>(U+002A)</td>
034 * <td>U+F02A</td>
035 * </tr>
036 * <tr>
037 * <td>/ (U+002F)</td>
038 * <td>U+F02F</td>
039 * </tr>
040 * <tr>
041 * <td>: (U+003A)</td>
042 * <td>U+F03A</td>
043 * </tr>
044 * <tr>
045 * <td>[ (U+005B)</td>
046 * <td>U+F05B</td>
047 * </tr>
048 * <tr>
049 * <td>] (U+005D)</td>
050 * <td>U+F05D</td>
051 * </tr>
052 * <tr>
053 * <td>| (U+007C)</td>
054 * <td>U+F07C</td>
055 * </tr>
056 * </table>
057 * </p>
058 */
059@Immutable
060public class Jsr283Encoder implements TextEncoder, TextDecoder {
061
062    public static boolean containsEncodeableCharacters( String str ) {
063        CharacterIterator iter = new StringCharacterIterator(str);
064        for (char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) {
065            if (c == '*' || c == '/' || c == ':' || c == '[' || c == ']' || c == '|') return true;
066        }
067        return false;
068
069    }
070
071    @Override
072    public String encode( String publicName ) {
073        if (publicName == null) return null;
074        StringBuilder sb = new StringBuilder();
075        CharacterIterator iter = new StringCharacterIterator(publicName);
076        for (char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) {
077            char mapped = c;
078            if (c == '*') {
079                mapped = '\uF02A';
080            } else if (c == '/') {
081                mapped = '\uF02F';
082            } else if (c == ':') {
083                mapped = '\uF03A';
084            } else if (c == '[') {
085                mapped = '\uF05B';
086            } else if (c == ']') {
087                mapped = '\uF05D';
088            } else if (c == '|') {
089                mapped = '\uF07C';
090            }
091            sb.append(mapped);
092        }
093        return sb.toString();
094    }
095
096    @Override
097    public String decode( String jcrNodeName ) {
098        if (jcrNodeName == null) return null;
099        StringBuilder sb = new StringBuilder();
100        CharacterIterator iter = new StringCharacterIterator(jcrNodeName);
101        for (char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) {
102            char mapped = c;
103            if (c == '\uF02A') {
104                mapped = '*';
105            } else if (c == '\uF02F') {
106                mapped = '/';
107            } else if (c == '\uF03A') {
108                mapped = ':';
109            } else if (c == '\uF05B') {
110                mapped = '[';
111            } else if (c == '\uF05D') {
112                mapped = ']';
113            } else if (c == '\uF07C') {
114                mapped = '|';
115            }
116            sb.append(mapped);
117
118        }
119        return sb.toString();
120    }
121
122}