/*
 * Copyright 2012 zhongl
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.github.zhongl.util;

import com.google.common.io.Files;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.security.AccessController;
import java.security.PrivilegedAction;

/**
 * {@link DirectByteBufferCleaner}
 *
 * @author <a href=mailto:zhong.lunfu@gmail.com>zhongl</a>
 * @created 2011-1-14
 */
public final class DirectByteBufferCleaner {
    private DirectByteBufferCleaner() {}

    public static void clean(final ByteBuffer buffer) {
        if (!buffer.isDirect()) return;
        try {
            invoke(invoke(viewed(buffer), "cleaner"), "clean");
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
    }

    public static boolean isNotCleaned(ByteBuffer buffer) {
        if (!buffer.isDirect()) return false;
        try {
            Long address = (Long) get(get(invoke(viewed(buffer), "cleaner"), "thunk"), "address");
            return address != 0L;
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
    }

    private static Object invoke(final Object target, final String methodName, final Class<?>... args) throws Exception {
        return AccessController.doPrivileged(new PrivilegedAction<Object>() {
            @Override
            public Object run() {
                try {
                    Method method = method(target, methodName, args);
                    method.setAccessible(true);
                    return method.invoke(target);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        });
    }

    private static Method method(Object target, String methodName, Class<?>[] args) throws NoSuchMethodException {
        try {
            return target.getClass().getMethod(methodName, args);
        } catch (NoSuchMethodException e) {
            return target.getClass().getDeclaredMethod(methodName, args);
        }
    }

    private static Object get(final Object target, final String fieldName) throws Exception {
        try {
            return AccessController.doPrivileged(new PrivilegedAction<Object>() {
                @Override
                public Object run() {
                    try {
                        Field field = field(target, fieldName);
                        field.setAccessible(true);
                        return field.get(target);
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }
            });
        } catch (Exception e) {
            System.out.println(fieldName);
            System.out.println(target.getClass());
            throw e;
        }
    }

    private static Field field(Object target, String fieldName) throws NoSuchFieldException {
        try {
            return target.getClass().getField(fieldName);
        } catch (NoSuchFieldException e) {
            return target.getClass().getDeclaredField(fieldName);
        }
    }

    public static void main(String[] args) throws Exception {
        File test = new File("test");

        try {
            Files.write("hello".getBytes(), test);
            MappedByteBuffer map = Files.map(test);
            clean(map);
            System.out.println(get(get(invoke(viewed(map), "cleaner"), "thunk"), "address"));
        } finally {
            test.delete();
        }
    }

    private static ByteBuffer viewed(ByteBuffer buffer) throws Exception {
        ByteBuffer viewedBuffer = (ByteBuffer) invoke(buffer, "viewedBuffer");
        if (viewedBuffer == null) return buffer;
        else return viewed(viewedBuffer);
    }
}

@@ -645,65 +645,8 @@
 l;%0A%0A
-import com.google.common.io.Files;%0A%0Aimport java.io.File;%0A
 impo
@@ -738,42 +738,8 @@
 er;%0A
-import java.nio.MappedByteBuffer;%0A
 impo
@@ -2240,338 +2240,8 @@
  %7D%0A%0A
-    private static Method method(Object target, String methodName, Class%3C?%3E%5B%5D args) throws NoSuchMethodException %7B%0A        try %7B%0A            return target.getClass().getMethod(methodName, args);%0A        %7D catch (NoSuchMethodException e) %7B%0A            return target.getClass().getDeclaredMethod(methodName, args);%0A        %7D%0A    %7D%0A%0A
     
@@ -2330,34 +2330,16 @@
 ption %7B%0A
-        try %7B%0A    
         
@@ -2412,36 +2412,32 @@
 ) %7B%0A            
-    
 @Override%0A      
@@ -2422,36 +2422,32 @@
       @Override%0A
-    
             publ
@@ -2476,38 +2476,30 @@
             
-    
 try %7B%0A
-    
             
@@ -2566,20 +2566,16 @@
         
-    
 field.se
@@ -2609,28 +2609,24 @@
             
-    
 return field
@@ -2647,36 +2647,32 @@
                 
-    
 %7D catch (Excepti
@@ -2671,36 +2671,32 @@
 (Exception e) %7B%0A
-    
                 
@@ -2742,34 +2742,26 @@
             
-    %7D%0A    
+%7D%0A
             
@@ -2774,173 +2774,12 @@
     
-    
 %7D);%0A
-        %7D catch (Exception e) %7B%0A            System.out.println(fieldName);%0A            System.out.println(target.getClass());%0A            throw e;%0A        %7D%0A
     
@@ -2800,26 +2800,28 @@
  static 
-Field fiel
+Method metho
 d(Object
@@ -2832,33 +2832,51 @@
 get, String 
-fieldName
+methodName, Class%3C?%3E%5B%5D args
 ) throws NoS
@@ -2874,28 +2874,29 @@
 hrows NoSuch
-Fiel
+Metho
 dException %7B
@@ -2950,31 +2950,39 @@
 ss().get
-Field(fieldName
+Method(methodName, args
 );%0A     
@@ -2999,20 +2999,21 @@
  (NoSuch
-Fiel
+Metho
 dExcepti
@@ -3072,23 +3072,31 @@
 ared
-Field(fieldName
+Method(methodName, args
 );%0A 
@@ -3112,37 +3112,38 @@
     %7D%0A%0A    p
-ublic
+rivate
  static 
 void main(St
@@ -3134,370 +3134,261 @@
 tic 
-void main(String%5B%5D args) throws Exception %7B%0A        File test = new File(%22test%22);%0A%0A        try %7B%0A            Files.write(%22hello%22.getBytes(), test);%0A            MappedByteBuffer map = Files.map(test);%0A            clean(map);%0A            System.out.println(get(get(invoke(viewed(map), %22cleaner%22), %22thunk%22), %22address%22));%0A        %7D finally %7B%0A            test.delete(
+Field field(Object target, String fieldName) throws NoSuchFieldException %7B%0A        try %7B%0A            return target.getClass().getField(fieldName);%0A        %7D catch (NoSuchFieldException e) %7B%0A            return target.getClass().getDeclaredField(fieldName
 );%0A 

Clean code.