View Javadoc

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 }