1 /*** 2 * Copyright 2006 Joseph M. Ferner 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.fernsroth.easyio.field.handler; 17 18 import java.io.IOException; 19 import java.lang.annotation.Annotation; 20 import java.lang.reflect.Field; 21 import java.lang.reflect.InvocationTargetException; 22 import java.lang.reflect.Method; 23 import java.util.Queue; 24 25 import com.fernsroth.easyio.EasyIOInputStream; 26 import com.fernsroth.easyio.EasyIOOutputStream; 27 import com.fernsroth.easyio.FieldHandler; 28 import com.fernsroth.easyio.exception.EasyIOException; 29 import com.fernsroth.easyio.exception.NestedEasyIOException; 30 import com.fernsroth.easyio.util.BeanUtils; 31 import com.fernsroth.easyio.util.EasyIOUtils; 32 33 /*** 34 * 35 * @author Joseph M. Ferner (Near Infinity Corporation) 36 */ 37 public abstract class FieldNumberHandlerBase implements FieldHandler { 38 39 /*** 40 * the number of bits in this number. 41 */ 42 private int bits; 43 44 /*** 45 * true, for unsigned. false, if not unsigned. 46 * TODO use this field. 47 */ 48 @SuppressWarnings("unused") 49 private boolean unsigned; 50 51 /*** 52 * constructor. 53 * @param bits the number of bits in this number. 54 * @param unsigned true, for unsigned. false, if not unsigned. 55 */ 56 public FieldNumberHandlerBase(int bits, boolean unsigned) { 57 this.bits = bits; 58 this.unsigned = unsigned; 59 } 60 61 /*** 62 * {@inheritDoc} 63 */ 64 public void read(EasyIOInputStream in, Object obj, Queue<Field> fieldsQueue) 65 throws EasyIOException, IOException { 66 try { 67 Object val = read(in); 68 int bitsLeft = this.bits; 69 int fieldBits = 0; 70 while (true) { 71 if (fieldsQueue.isEmpty()) { 72 break; 73 } 74 Field field = fieldsQueue.peek(); 75 Annotation fieldAnnotation = getAnnotation(field); 76 if (fieldAnnotation == null) { 77 break; 78 } 79 fieldBits = getFieldBits(field, fieldAnnotation); 80 if (bitsLeft < fieldBits) { 81 break; 82 } 83 fieldsQueue.remove(); 84 85 Object maskedVal; 86 if (fieldBits == this.bits) { 87 maskedVal = val; 88 } else { 89 maskedVal = EasyIOUtils.mask(val, fieldBits); 90 } 91 BeanUtils.setValue(obj, field, maskedVal); 92 if (fieldBits != this.bits) { 93 val = EasyIOUtils.shift(val, fieldBits); 94 } 95 bitsLeft -= fieldBits; 96 } 97 98 } catch (IllegalArgumentException e) { 99 throw new NestedEasyIOException(e); 100 } 101 } 102 103 /*** 104 * {@inheritDoc} 105 */ 106 public void write(EasyIOOutputStream out, Object obj, 107 Queue<Field> fieldsQueue) throws EasyIOException, IOException { 108 try { 109 Object val = createZero(); 110 int bitsLeft = this.bits; 111 int fieldBits = 0; 112 while (true) { 113 if (fieldsQueue.isEmpty()) { 114 break; 115 } 116 Field field = fieldsQueue.peek(); 117 Annotation fieldAnnotation = getAnnotation(field); 118 if (fieldAnnotation == null) { 119 break; 120 } 121 fieldBits = getFieldBits(field, fieldAnnotation); 122 if (bitsLeft < fieldBits) { 123 break; 124 } 125 fieldsQueue.remove(); 126 127 Object fieldVal = BeanUtils.getValue(obj, field); 128 if (fieldBits == this.bits) { 129 val = fieldVal; 130 } else { 131 Object maskedVal = EasyIOUtils.mask(fieldVal, fieldBits); 132 maskedVal = EasyIOUtils.shift(maskedVal, bitsLeft 133 - this.bits); 134 val = EasyIOUtils.or(val, maskedVal); 135 } 136 bitsLeft -= fieldBits; 137 } 138 139 write(out, val); 140 141 } catch (IllegalArgumentException e) { 142 throw new NestedEasyIOException(e); 143 } 144 } 145 146 /*** 147 * writes a value to the output stream. 148 * @param out the output stream to write to. 149 * @param val the value to write. 150 * @throws IOException 151 */ 152 protected abstract void write(EasyIOOutputStream out, Object val) 153 throws IOException; 154 155 /*** 156 * creates an object with the value of zero. 157 * @return the new zero object. 158 */ 159 protected abstract Object createZero(); 160 161 /*** 162 * read data from the input stream. 163 * @param in the input stream to read from. 164 * @return the read object. 165 * @throws IOException 166 */ 167 protected abstract Object read(EasyIOInputStream in) throws IOException; 168 169 /*** 170 * gets the number of bits for a given field. 171 * @param field the field to get the number of bits from/ 172 * @param fieldAnnotation the field annotation. 173 * @return the number of bits. 174 */ 175 protected int getFieldBits(Field field, Annotation fieldAnnotation) { 176 try { 177 Class<? extends Annotation> anType = fieldAnnotation 178 .annotationType(); 179 Method bitsMethod = anType.getMethod("bits", (Class[]) null); 180 return (Integer) bitsMethod 181 .invoke(fieldAnnotation, (Object[]) null); 182 } catch (IllegalArgumentException e) { 183 throw new RuntimeException(fieldAnnotation.getClass().getName() 184 + " must have a 'bits' field", e); 185 } catch (IllegalAccessException e) { 186 throw new RuntimeException(fieldAnnotation.getClass().getName() 187 + " must have a 'bits' field", e); 188 } catch (InvocationTargetException e) { 189 throw new RuntimeException(fieldAnnotation.getClass().getName() 190 + " must have a 'bits' field", e); 191 } catch (SecurityException e) { 192 throw new RuntimeException(fieldAnnotation.getClass().getName() 193 + " must have a 'bits' field", e); 194 } catch (NoSuchMethodException e) { 195 throw new RuntimeException(fieldAnnotation.getClass().getName() 196 + " must have a 'bits' field", e); 197 } 198 } 199 200 /*** 201 * @param field the field to get the annotation from. 202 * @return the retrieved annotation. 203 */ 204 protected abstract Annotation getAnnotation(Field field); 205 206 }