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 }