001/**
002 *   GRANITE DATA SERVICES
003 *   Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S.
004 *
005 *   This file is part of the Granite Data Services Platform.
006 *
007 *   Granite Data Services is free software; you can redistribute it and/or
008 *   modify it under the terms of the GNU Lesser General Public
009 *   License as published by the Free Software Foundation; either
010 *   version 2.1 of the License, or (at your option) any later version.
011 *
012 *   Granite Data Services is distributed in the hope that it will be useful,
013 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
014 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
015 *   General Public License for more details.
016 *
017 *   You should have received a copy of the GNU Lesser General Public
018 *   License along with this library; if not, write to the Free Software
019 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
020 *   USA, or see <http://www.gnu.org/licenses/>.
021 */
022package org.granite.messaging.jmf.codec.std.impl.util;
023
024import java.io.IOException;
025import java.io.OutputStream;
026import java.util.ArrayList;
027import java.util.List;
028
029import org.granite.messaging.jmf.InputContext;
030import org.granite.messaging.jmf.OutputContext;
031
032/**
033 * @author Franck WOLFF
034 */
035public class ClassNameUtil {
036        
037        private static final char[] ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$.".toCharArray();
038        
039        private static final byte[] ALPHABET_INDICES = new byte[128]; 
040        static {
041                for (int i = 0; i < ALPHABET_INDICES.length; i++)
042                        ALPHABET_INDICES[i] = -1;
043                for (int i = 0; i < ALPHABET.length; i++)
044                        ALPHABET_INDICES[ALPHABET[i]] = (byte)i;
045        }
046
047        private static final int CLASS_NAME_END = ALPHABET.length;
048        private static final int INDEX_0 = CLASS_NAME_END + 1;
049        private static final int INDEX_OVERFLOW = 0xFF - INDEX_0;
050        
051        private ClassNameUtil() {
052                throw new UnsupportedOperationException("Not instantiable");
053        }
054        
055        public static void initClassNameDictionary(List<String> dictionary, String className) {
056                if (!dictionary.contains(className)) {
057                        List<String> lookups = new ArrayList<String>();
058                        
059                        String lookup = className;
060                        do {
061                                lookups.add(lookup);
062                                int lastIndexOfDot = lookup.lastIndexOf('.');
063                                if (lastIndexOfDot == -1) {
064                                        lookup = "";
065                                        break;
066                                }
067                                lookup = lookup.substring(0, lastIndexOfDot);
068                        }
069                        while (!dictionary.contains(lookup));
070                        
071                        for (int i = lookups.size() - 1; i >= 0; i--)
072                                dictionary.add(lookups.get(i));
073                }
074        }
075        
076        public static void encodeClassName(OutputContext ctx, String className) throws IOException {
077                final OutputStream os = ctx.getOutputStream();
078                
079                String lookup = className;
080                
081                int indexOfStoredClassName = ctx.indexOfClassName(lookup);
082                if (indexOfStoredClassName == -1) {
083                        List<String> lookups = new ArrayList<String>();
084                        
085                        do {
086                                lookups.add(lookup);
087                                int lastIndexOfDot = lookup.lastIndexOf('.');
088                                if (lastIndexOfDot == -1) {
089                                        lookup = "";
090                                        break;
091                                }
092                                lookup = lookup.substring(0, lastIndexOfDot);
093                                indexOfStoredClassName = ctx.indexOfClassName(lookup);
094                        }
095                        while (indexOfStoredClassName == -1);
096                        
097                        for (int i = lookups.size() - 1; i >= 0; i--)
098                                ctx.addToClassNames(lookups.get(i));
099                }
100                
101                if (indexOfStoredClassName != -1) {
102                        if (indexOfStoredClassName < INDEX_OVERFLOW)
103                                os.write(indexOfStoredClassName + INDEX_0);
104                        else {
105                                os.write(0xFF);
106                                IntegerUtil.encodeVariableUnsignedInteger(ctx, indexOfStoredClassName - INDEX_OVERFLOW);
107                        }
108                }
109                
110                if (lookup.length() != className.length()) {
111                        int start = (lookup.length() == 0 ? 0 : lookup.length() + 1);
112                        for (int i = start; i < className.length(); i++) {
113                                int code = ALPHABET_INDICES[className.charAt(i)];
114                                if (code == -1)
115                                        throw new RuntimeException();
116                                os.write(code);
117                        }
118                }
119
120                os.write(CLASS_NAME_END);
121        }
122        
123        public static String decodeClassName(InputContext ctx) throws IOException {
124                String className = "";
125                
126                int code = ctx.safeRead();
127                if (code > CLASS_NAME_END) {
128                        if (code != 0xFF)
129                                className = ctx.getClassName(code - INDEX_0);
130                        else {
131                                int index = IntegerUtil.decodeVariableUnsignedInteger(ctx) + INDEX_OVERFLOW;
132                                className = ctx.getClassName(index);
133                        }
134                        
135                        code = ctx.safeRead();
136                }
137                
138                if (code != CLASS_NAME_END) {
139                        StringBuilder sb = (className.length() > 0 ? new StringBuilder(className).append('.') : new StringBuilder(64));
140                        do {
141                                char c = ALPHABET[code];
142                                if (c == '.')
143                                        ctx.addToClassNames(sb.toString());
144                                sb.append(c);
145                                code = ctx.safeRead();
146                        }
147                        while (code != CLASS_NAME_END);
148                        
149                        className = sb.toString();
150                        
151                        ctx.addToClassNames(className);
152                }
153                
154                return className;
155        }
156}