001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 *
019 */
020 package org.apache.directory.shared.asn1.codec.stateful.examples;
021
022
023 import java.nio.ByteBuffer;
024
025 import org.apache.directory.shared.asn1.codec.DecoderException;
026 import org.apache.directory.shared.asn1.codec.stateful.AbstractStatefulDecoder;
027
028
029 /**
030 * Document me.
031 *
032 * @author <a href="mailto:dev@directory.apache.org"> Apache Directory Project</a>
033 * @version $Rev: 664290 $
034 */
035 public class HexDecoder extends AbstractStatefulDecoder
036 {
037 private ByteBuffer decoded = ByteBuffer.allocate( 128 );
038
039 private byte lsn;
040
041 private byte msn;
042
043 private boolean expectingMsn = true;
044
045
046 public void decode( Object chunk ) throws DecoderException
047 {
048 ByteBuffer encoded = ( ByteBuffer ) chunk;
049
050 if ( encoded == null || !encoded.hasRemaining() )
051 {
052 return;
053 }
054
055 while ( encoded.hasRemaining() )
056 {
057 if ( !decoded.hasRemaining() )
058 {
059 decoded.flip();
060 super.decodeOccurred( decoded );
061 decoded.clear();
062 }
063
064 if ( expectingMsn )
065 {
066 msn = encoded.get();
067 expectingMsn = false;
068 }
069 else
070 {
071 lsn = encoded.get();
072 expectingMsn = true;
073 }
074
075 /*
076 * if we've hit the most significant nibble then we have two hex
077 * characters as bytes so we need to compute and add the byte to the
078 * buffer
079 */
080 if ( expectingMsn )
081 {
082 byte bite = getNibble( lsn );
083 bite |= ( getNibble( msn ) << 4 );
084 decoded.put( bite );
085 }
086 }
087
088 /*
089 * only trigger a decode callback if we have seen an even number of hex
090 * character bytes in which case we're in the expectingMsn state this
091 * will flush out what's siting in the buffer automatically
092 */
093 if ( expectingMsn )
094 {
095 decoded.flip();
096 super.decodeOccurred( decoded );
097 decoded.clear();
098 }
099 }
100
101
102 private byte getNibble( byte ch ) throws DecoderException
103 {
104 // lowercase the character if it is in upper case
105 if ( ch > 64 && ch < 91 )
106 {
107 ch -= 32;
108 }
109
110 switch ( ch )
111 {
112 case 48:
113 return 0;
114 case 49:
115 return 1;
116 case 50:
117 return 2;
118 case 51:
119 return 3;
120 case 52:
121 return 4;
122 case 53:
123 return 5;
124 case 54:
125 return 6;
126 case 55:
127 return 7;
128 case 56:
129 return 8;
130 case 57:
131 return 9;
132 case 97:
133 return 10;
134 case 98:
135 return 11;
136 case 99:
137 return 12;
138 case 100:
139 return 13;
140 case 101:
141 return 14;
142 case 102:
143 return 15;
144 default:
145 throw new DecoderException( "non-hex character '" + ( char ) ch + "' encountered" );
146 }
147 }
148 }