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;
023
024import java.io.IOException;
025import java.io.OutputStream;
026
027import org.granite.messaging.jmf.DumpContext;
028import org.granite.messaging.jmf.InputContext;
029import org.granite.messaging.jmf.OutputContext;
030import org.granite.messaging.jmf.codec.std.StringCodec;
031
032/**
033 * @author Franck WOLFF
034 */
035public class StringCodecImpl extends AbstractIntegerStringCodec<String> implements StringCodec {
036        
037        protected static final int UUID_FLAG = 0x10;
038        protected static final int UUID_UPPERCASE_FLAG = 0x20;
039        
040        protected static final char[] LOWER_HEX = "0123456789abcdef".toCharArray();
041        protected static final char[] UPPER_HEX = "0123456789ABCDEF".toCharArray();
042        
043        protected static final int[] HEX_INDICES = new int[256];
044        static {
045                for (int i = 0; i < HEX_INDICES.length; i++) {
046                        if (i >= '0' && i <= '9')
047                                HEX_INDICES[i] = i - '0';
048                        else if (i >= 'a' && i <= 'f')
049                                HEX_INDICES[i] = i - 'a' + 10;
050                        else if (i >= 'A' && i <= 'F')
051                                HEX_INDICES[i] = i - 'A' + 10;
052                        else
053                                HEX_INDICES[i] = -1;
054                }
055        }
056        
057        public int getObjectType() {
058                return JMF_STRING;
059        }
060
061        public Class<?> getObjectClass() {
062                return String.class;
063        }
064
065        public void encode(OutputContext ctx, String v) throws IOException {
066                
067                int uuidCaseFlag = isUUID(v);
068                if (uuidCaseFlag != -1 && ctx.indexOfStoredStrings(v) < 0) {
069                        encodeUUID(ctx, v, uuidCaseFlag);
070                        ctx.addToStoredStrings(v);
071                }
072                else
073                        writeString(ctx, v, JMF_STRING_TYPE_HANDLER);
074        }
075        
076        public String decode(InputContext ctx, int parameterizedJmfType) throws IOException {
077                int jmfType = ctx.getSharedContext().getCodecRegistry().extractJmfType(parameterizedJmfType);
078                
079                if (jmfType != JMF_STRING)
080                        throw newBadTypeJMFEncodingException(jmfType, parameterizedJmfType);
081                
082                if ((parameterizedJmfType & UUID_FLAG) != 0) {
083                        String uid = decodeUUID(ctx, parameterizedJmfType);
084                        ctx.addSharedString(uid);
085                        return uid;
086                }
087                
088                return readString(ctx, parameterizedJmfType, JMF_STRING_TYPE_HANDLER);
089        }
090        
091        public void dump(DumpContext ctx, int parameterizedJmfType) throws IOException {
092                ctx.indentPrintLn(String.class.getName() + ": \"" + escape(decode(ctx, parameterizedJmfType)) + "\"");
093        }
094        
095        protected int isUUID(String v) {
096                if (v.length() != 36 || v.charAt(8) != '-')
097                        return -1;
098                
099                int flag = -1;
100                
101                for (int i = 0; i < 36; i++) {
102                        char c = v.charAt(i);
103                        
104                        switch (i) {
105                        case 8: case 13: case 18: case 23:
106                                if (c != '-')
107                                        return -1;
108                                break;
109                        default:
110                                if (!(c >= '0' && c <= '9')) {
111                                        if (c >= 'a' && c <= 'f') {
112                                                if (flag == -1)
113                                                        flag = 0x00;
114                                                else if (flag != 0x00)
115                                                        return -1;
116                                        }
117                                        else if (c >= 'A' && c <= 'F') {
118                                                if (flag == -1)
119                                                        flag = UUID_UPPERCASE_FLAG;
120                                                else if (flag != UUID_UPPERCASE_FLAG)
121                                                        return -1;
122                                        }
123                                        else
124                                                return -1;
125                                }
126                                break;
127                        }
128                }
129                
130                if (flag == -1)
131                        flag = 0x00;
132                
133                return flag;
134        }
135        
136        protected void encodeUUID(OutputContext ctx, String v, int caseFlag) throws IOException {
137                final OutputStream os = ctx.getOutputStream();
138                
139                os.write(caseFlag | UUID_FLAG | JMF_STRING);
140                
141                byte[] bytes = new byte[16];
142
143                int i = 0, j = 0;
144                while (i < 36) {
145                        char c1 = v.charAt(i++);
146                        if (c1 == '-')
147                                c1 = v.charAt(i++);
148                        char c2 = v.charAt(i++);
149                        
150                        bytes[j++] = (byte)(HEX_INDICES[c1] << 4 | HEX_INDICES[c2]);
151                }
152
153                os.write(bytes);
154        }
155        
156        protected String decodeUUID(InputContext ctx, int parameterizedJmfType) throws IOException {
157                final char[] hex = (parameterizedJmfType & UUID_UPPERCASE_FLAG) != 0 ? UPPER_HEX : LOWER_HEX;
158                
159                byte[] bytes = new byte[16];
160                ctx.safeReadFully(bytes);
161                
162                char[] chars = new char[36];
163                int i = 0;
164                for (byte b : bytes) {
165                        if (i == 8 || i == 13 || i == 18 || i == 23)
166                                chars[i++] = '-';
167                        chars[i++] = hex[(b & 0xF0) >> 4];
168                        chars[i++] = hex[b & 0x0F];
169                }
170                
171                return new String(chars);
172        }
173}