001/*
002 *  Copyright 2013 Christopher Pheby
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.jadira.reflection.access.unsafe;
017
018import java.lang.reflect.Field;
019
020import org.jadira.reflection.access.api.FieldAccess;
021
022/**
023 * FieldAccess implementation based on sun.misc.Unsafe
024 * @param <C> The Class containing the Field to be accessed
025 */
026public class UnsafeFieldAccess<C> implements FieldAccess<C> {
027
028        private static final UnsafeOperations UNSAFE_OPERATIONS = UnsafeOperations.getUnsafeOperations();
029
030        private Field field;
031        private Class<C> declaringClass;
032        private Class<?> type;
033        private long fieldOffset;
034
035        @SuppressWarnings("unchecked")
036        private UnsafeFieldAccess(Field f) {
037                this.field = f;
038                this.declaringClass = (Class<C>) f.getDeclaringClass();
039                this.type = (Class<?>) f.getType();
040                this.fieldOffset = UNSAFE_OPERATIONS.getObjectFieldOffset(f);
041        }
042
043        @Override
044        public Class<C> declaringClass() {
045                return declaringClass;
046        }
047        
048        @Override
049        public Class<?> fieldClass() {
050                return type;
051        }
052        
053        @Override
054        public Field field() {
055                return field;
056        }
057        
058        /**
059         * Get the offset position (in bytes) for this field
060         * @return Offset as a long
061         */
062        public long fieldOffset() {
063                return fieldOffset;
064        }
065        
066        @Override
067        public Object getValue(C parent) {
068                return UNSAFE_OPERATIONS.getObject(parent, fieldOffset);
069        }
070
071        @Override
072        public boolean getBooleanValue(C parent) {
073                throw new UnsupportedOperationException("Not supported for this field type");
074        }
075        
076        @Override
077        public byte getByteValue(C parent) {
078                throw new UnsupportedOperationException("Not supported for this field type");
079        }
080        
081        @Override
082        public char getCharValue(C parent) {
083                throw new UnsupportedOperationException("Not supported for this field type");
084        }
085        
086        @Override
087        public short getShortValue(C parent) {
088                throw new UnsupportedOperationException("Not supported for this field type");
089        }
090        
091        @Override
092        public int getIntValue(C parent) {
093                throw new UnsupportedOperationException("Not supported for this field type");
094        }
095        
096        @Override
097        public long getLongValue(C parent) {
098                throw new UnsupportedOperationException("Not supported for this field type");
099        }
100        
101        @Override
102        public float getFloatValue(C parent) {
103                throw new UnsupportedOperationException("Not supported for this field type");
104        }
105        
106        @Override
107        public double getDoubleValue(C parent) {
108                throw new UnsupportedOperationException("Not supported for this field type");
109        }
110        
111        @Override
112        public void putValue(C parent, Object newFieldValue) {
113                if (newFieldValue == null) {
114                        UNSAFE_OPERATIONS.putNullObject(parent, fieldOffset);
115                } else {
116                        UNSAFE_OPERATIONS.putObject(parent, fieldOffset, newFieldValue);
117                }
118        }
119        
120        @Override
121        public void putBooleanValue(C parent, boolean newFieldValue) {
122                throw new UnsupportedOperationException("Not supported for this field type");
123        }
124        
125        @Override
126        public void putByteValue(C parent, byte newFieldValue) {
127                throw new UnsupportedOperationException("Not supported for this field type");
128        }
129        
130        @Override
131        public void putCharValue(C parent, char newFieldValue) {
132                throw new UnsupportedOperationException("Not supported for this field type");
133        }
134        
135        @Override
136        public void putShortValue(C parent, short newFieldValue) {
137                throw new UnsupportedOperationException("Not supported for this field type");
138        }
139        
140        @Override
141        public void putIntValue(C parent, int newFieldValue) {
142                throw new UnsupportedOperationException("Not supported for this field type");
143        }
144        
145        @Override
146        public void putLongValue(C parent, long newFieldValue) {
147                throw new UnsupportedOperationException("Not supported for this field type");
148        }
149        
150        @Override
151        public void putFloatValue(C parent, float newFieldValue) {
152                throw new UnsupportedOperationException("Not supported for this field type");
153        }
154        
155        @Override
156        public void putDoubleValue(C parent, double newFieldValue) {
157                throw new UnsupportedOperationException("Not supported for this field type");
158        }
159        
160        /**
161         * Get a new instance that can access the given Field
162         * @param f Field to be accessed
163         * @param <C> The type of class containing the field to be accessed
164         * @return New UnsafeFieldAccess instance
165         */
166        public static <C> UnsafeFieldAccess<C> get(Field f) {
167                
168                Class<?> type = f.getType();
169                
170                if (type.isPrimitive()) {
171                        if (java.lang.Boolean.TYPE == type) {
172                                return new UnsafeBooleanFieldAccess<C>(f);
173                        } else if (java.lang.Byte.TYPE == type) {
174                                return new UnsafeByteFieldAccess<C>(f);
175                        } else if (java.lang.Character.TYPE == type) {
176                                return new UnsafeCharFieldAccess<C>(f);
177                        } else if (java.lang.Short.TYPE == type) {
178                                return new UnsafeShortFieldAccess<C>(f);
179                        } else if (java.lang.Integer.TYPE == type) {
180                                return new UnsafeIntFieldAccess<C>(f);
181                        } else if (java.lang.Long.TYPE == type) {
182                                return new UnsafeLongFieldAccess<C>(f);
183                        } else if (java.lang.Float.TYPE == type) {
184                                return new UnsafeFloatFieldAccess<C>(f);
185                        } else if (java.lang.Double.TYPE == type) {
186                                return new UnsafeDoubleFieldAccess<C>(f);
187                        }
188                }
189                return new UnsafeFieldAccess<C>(f);
190        }
191        
192        /**
193         * UnsafeFieldAccess implementation suitable for accessing boolean fields
194         * @param <C> The Class containing the Field to be accessed
195         */
196        public static class UnsafeBooleanFieldAccess<C> extends UnsafeFieldAccess<C> {
197
198                /**
199                 * Construct a new instance for the given Field
200                 * @param f The Field to be accessed
201                 */
202                public UnsafeBooleanFieldAccess(Field f) {
203                        super(f);
204                }
205
206                @Override
207                public Boolean getValue(C parent) {
208                        return Boolean.valueOf(getBooleanValue(parent));
209                }
210                
211                @Override
212                public boolean getBooleanValue(C parent) {
213                        return UNSAFE_OPERATIONS.getBoolean(parent, super.fieldOffset);
214                }
215                
216                @Override
217                public void putValue(C parent, Object newFieldValue) {
218                        if (newFieldValue instanceof Boolean) {
219                                putBooleanValue(parent, ((Boolean)newFieldValue).booleanValue());
220                        } else {
221                                throw new IllegalArgumentException("Only a boolean value can be supplied to a boolean field");
222                        }
223                }
224                
225                @Override
226                public void putBooleanValue(C parent, boolean newFieldValue) {
227                        UNSAFE_OPERATIONS.putBoolean(parent, super.fieldOffset, newFieldValue);
228                }
229        }
230        
231        /**
232         * UnsafeFieldAccess implementation suitable for accessing byte fields
233         * @param <C> The Class containing the Field to be accessed
234         */
235        public static class UnsafeByteFieldAccess<C> extends UnsafeFieldAccess<C> {
236
237                /**
238                 * Construct a new instance for the given Field
239                 * @param f The Field to be accessed
240                 */
241                public UnsafeByteFieldAccess(Field f) {
242                        super(f);
243                }
244
245                @Override
246                public Byte getValue(C parent) {
247                        return Byte.valueOf(getByteValue(parent));
248                }
249                
250                @Override
251                public byte getByteValue(C parent) {
252                        return UNSAFE_OPERATIONS.getByte(parent, super.fieldOffset);
253                }
254                
255                @Override
256                public void putValue(C parent, Object newFieldValue) {
257                        if (newFieldValue instanceof Byte) {
258                                putByteValue(parent, ((Byte)newFieldValue).byteValue());
259                        } else {
260                                throw new IllegalArgumentException("Only a byte value can be supplied to a byte field");
261                        }
262                }
263                
264                @Override
265                public void putByteValue(C parent, byte newFieldValue) {
266                        UNSAFE_OPERATIONS.putByte(parent, super.fieldOffset, newFieldValue);
267                }
268        }
269        
270        /**
271         * UnsafeFieldAccess implementation suitable for accessing char fields
272         * @param <C> The Class containing the Field to be accessed
273         */
274        public static class UnsafeCharFieldAccess<C> extends UnsafeFieldAccess<C> {
275
276                /**
277                 * Construct a new instance for the given Field
278                 * @param f The Field to be accessed
279                 */
280                public UnsafeCharFieldAccess(Field f) {
281                        super(f);
282                }
283                
284                @Override
285                public Character getValue(C parent) {
286                        return Character.valueOf(getCharValue(parent));
287                }
288
289                @Override
290                public char getCharValue(C parent) {
291                        return UNSAFE_OPERATIONS.getChar(parent, super.fieldOffset);
292                }
293                
294                @Override
295                public void putValue(C parent, Object newFieldValue) {
296                        if (newFieldValue instanceof Character) {
297                                putCharValue(parent, ((Character)newFieldValue).charValue());
298                        } else {
299                                throw new IllegalArgumentException("Only a character value can be supplied to a char field");
300                        }
301                }
302                
303                @Override
304                public void putCharValue(C parent, char newFieldValue) {
305                        UNSAFE_OPERATIONS.putChar(parent, super.fieldOffset, newFieldValue);
306                }
307        }
308        
309        /**
310         * UnsafeFieldAccess implementation suitable for accessing short fields
311         * @param <C> The Class containing the Field to be accessed
312         */
313        public static class UnsafeShortFieldAccess<C> extends UnsafeFieldAccess<C> {
314
315                /**
316                 * Construct a new instance for the given Field
317                 * @param f The Field to be accessed
318                 */
319                public UnsafeShortFieldAccess(Field f) {
320                        super(f);
321                }
322
323                @Override
324                public Short getValue(C parent) {
325                        return Short.valueOf(getShortValue(parent));
326                }
327                
328                @Override
329                public short getShortValue(C parent) {
330                        return UNSAFE_OPERATIONS.getShort(parent, super.fieldOffset);
331                }
332                
333                @Override
334                public void putValue(C parent, Object newFieldValue) {
335                        if (newFieldValue instanceof Short) {
336                                putShortValue(parent, ((Short)newFieldValue).shortValue());
337                        } else if (newFieldValue instanceof Byte) {
338                                putShortValue(parent, ((Byte)newFieldValue).shortValue());
339                        } else {
340                                throw new IllegalArgumentException("Only a short, or byte value can be supplied to a short field");
341                        }
342                }
343                
344                @Override
345                public void putShortValue(C parent, short newFieldValue) {
346                        UNSAFE_OPERATIONS.putShort(parent, super.fieldOffset, newFieldValue);
347                }
348        }
349        
350        /**
351         * UnsafeFieldAccess implementation suitable for accessing int fields
352         * @param <C> The Class containing the Field to be accessed
353         */
354        public static class UnsafeIntFieldAccess<C> extends UnsafeFieldAccess<C> {
355
356                /**
357                 * Construct a new instance for the given Field
358                 * @param f The Field to be accessed
359                 */
360                public UnsafeIntFieldAccess(Field f) {
361                        super(f);
362                }
363                
364                @Override
365                public Integer getValue(C parent) {
366                        return Integer.valueOf(getIntValue(parent));
367                }
368
369                @Override
370                public int getIntValue(C parent) {
371                        return UNSAFE_OPERATIONS.getInt(parent, super.fieldOffset);
372                }
373                
374                @Override
375                public void putValue(C parent, Object newFieldValue) {
376                        if (newFieldValue instanceof Integer) {
377                                putIntValue(parent, ((Integer)newFieldValue).intValue());
378                        } else if (newFieldValue instanceof Short) {
379                                putIntValue(parent, ((Short)newFieldValue).intValue());
380                        } else if (newFieldValue instanceof Byte) {
381                                putIntValue(parent, ((Byte)newFieldValue).intValue());
382                        } else {
383                                throw new IllegalArgumentException("Only an integer, short, or byte value can be supplied to an int field");
384                        }
385                }
386                
387                @Override
388                public void putIntValue(C parent, int newFieldValue) {
389                        UNSAFE_OPERATIONS.putInt(parent, super.fieldOffset, newFieldValue);
390                }
391        }
392        
393        /**
394         * UnsafeFieldAccess implementation suitable for accessing long fields
395         * @param <C> The Class containing the Field to be accessed
396         */
397        public static class UnsafeLongFieldAccess<C> extends UnsafeFieldAccess<C> {
398
399                /**
400                 * Construct a new instance for the given Field
401                 * @param f The Field to be accessed
402                 */
403                public UnsafeLongFieldAccess(Field f) {
404                        super(f);
405                }
406
407                @Override
408                public Long getValue(C parent) {
409                        return Long.valueOf(getLongValue(parent));
410                }
411                
412                @Override
413                public long getLongValue(C parent) {
414                        return UNSAFE_OPERATIONS.getLong(parent, super.fieldOffset);
415                }
416                
417                @Override
418                public void putValue(C parent, Object newFieldValue) {
419                        if (newFieldValue instanceof Long) {
420                                putLongValue(parent, ((Long)newFieldValue).longValue());
421                        } else if (newFieldValue instanceof Integer) {
422                                putLongValue(parent, ((Integer)newFieldValue).longValue());
423                        } else if (newFieldValue instanceof Short) {
424                                putLongValue(parent, ((Short)newFieldValue).longValue());
425                        } else if (newFieldValue instanceof Byte) {
426                                putLongValue(parent, ((Byte)newFieldValue).longValue());
427                        } else {
428                                throw new IllegalArgumentException("Only a long, integer, short, or byte value can be supplied to a long field");
429                        }
430                }
431                
432                @Override
433                public void putLongValue(C parent, long newFieldValue) {
434                        UNSAFE_OPERATIONS.putLong(parent, super.fieldOffset, newFieldValue);
435                }
436        }
437        
438        /**
439         * UnsafeFieldAccess implementation suitable for accessing float fields
440         * @param <C> The Class containing the Field to be accessed
441         */
442        public static class UnsafeFloatFieldAccess<C> extends UnsafeFieldAccess<C> {
443
444                /**
445                 * Construct a new instance for the given Field
446                 * @param f The Field to be accessed
447                 */
448                public UnsafeFloatFieldAccess(Field f) {
449                        super(f);
450                }
451                
452                @Override
453                public Float getValue(C parent) {
454                        return Float.valueOf(getFloatValue(parent));
455                }
456
457                @Override
458                public float getFloatValue(C parent) {
459                        return UNSAFE_OPERATIONS.getFloat(parent, super.fieldOffset);
460                }
461                
462                @Override
463                public void putValue(C parent, Object newFieldValue) {
464                        if (newFieldValue instanceof Float) {
465                                putFloatValue(parent, ((Float)newFieldValue).floatValue());
466                        } else {
467                                throw new IllegalArgumentException("Only a float value can be supplied to a float field");
468                        }
469                }               
470                
471                @Override
472                public void putFloatValue(C parent, float newFieldValue) {
473                        UNSAFE_OPERATIONS.putFloat(parent, super.fieldOffset, newFieldValue);
474                }
475        }
476        
477        /**
478         * UnsafeFieldAccess implementation suitable for accessing double fields
479         * @param <C> The Class containing the Field to be accessed
480         */
481        public static class UnsafeDoubleFieldAccess<C> extends UnsafeFieldAccess<C> {
482
483                /**
484                 * Construct a new instance for the given Field
485                 * @param f The Field to be accessed
486                 */
487                public UnsafeDoubleFieldAccess(Field f) {
488                        super(f);
489                }
490
491                @Override
492                public Double getValue(C parent) {
493                        return Double.valueOf(getDoubleValue(parent));
494                }
495                
496                @Override
497                public double getDoubleValue(C parent) {
498                        return UNSAFE_OPERATIONS.getDouble(parent, super.fieldOffset);
499                }
500                
501                @Override
502                public void putValue(C parent, Object newFieldValue) {
503                        if (newFieldValue instanceof Double) {
504                                putDoubleValue(parent, ((Double)newFieldValue).doubleValue());
505                        } else {
506                                throw new IllegalArgumentException("Only a double value can be supplied to a double field");
507                        }
508                }
509                
510                @Override
511                public void putDoubleValue(C parent, double newFieldValue) {
512                        UNSAFE_OPERATIONS.putDouble(parent, super.fieldOffset, newFieldValue);
513                }
514        }
515}