001    /*
002     * To change this template, choose Tools | Templates
003     * and open the template in the editor.
004     */
005    package org.bridj.util;
006    
007    import java.io.IOException;
008    import java.lang.reflect.Method;
009    import java.lang.reflect.Modifier;
010    import java.util.HashMap;
011    import java.util.Map;
012    import java.util.Set;
013    import java.util.WeakHashMap;
014    import java.util.regex.Pattern;
015    import org.bridj.BridJ;
016    import org.bridj.Platform;
017    import org.bridj.Version;
018    
019    /**
020     *
021     * @author ochafik
022     */
023    public class JNIUtils {
024        
025        private static class NativeMethodsCache {
026            Map<String, String> signatures = new HashMap<String, String>();
027            public NativeMethodsCache(String internalClassName) throws IOException {
028                for (String[] sig : BytecodeAnalyzer.getNativeMethodSignatures(internalClassName, Platform.getClassLoader())) {
029                    signatures.put(sig[1], sig[2]);
030                }
031            }
032            public String get(String name) {
033                return signatures.get(name);
034            }
035            public Set<String> getNames() {
036                return signatures.keySet();
037            }
038        }
039        private static Map<String, NativeMethodsCache> nativeMethodsCache = new WeakHashMap<String, NativeMethodsCache>();
040        private static synchronized NativeMethodsCache getNativeMethodsCache(String internalClassName) throws IOException {
041            NativeMethodsCache cache = nativeMethodsCache.get(internalClassName);
042            if (cache == null)
043                nativeMethodsCache.put(internalClassName, cache = new NativeMethodsCache(internalClassName));
044            return cache;
045        }
046        private static final String bridjPackage = BridJ.class.getPackage().getName();
047        
048        private static final String bridjNormalPackagePrefix = bridjPackage.endsWith(Version.VERSION_SPECIFIC_SUB_PACKAGE) ? bridjPackage.substring(0, bridjPackage.length() - Version.VERSION_SPECIFIC_SUB_PACKAGE.length()) : bridjPackage + ".";
049        private static final String bridjVersionSpecificPackagePrefix = bridjPackage + ".";
050        
051        static int findLastNonEscapeUnderscore(String s) {
052            int len = s.length(), i = len;
053            do {
054                i = s.lastIndexOf("_", i - 1);
055                if (i >= 0 && (i == len - 1 || !Character.isDigit(s.charAt(i + 1))))
056                    return i;
057            } while (i > 0);
058            return -1;
059        }
060        public static String decodeVersionSpecificMethodNameClassAndSignature(String symbolName, Object[] nameAndSigArray) throws NoSuchMethodException, IOException {
061            return decodeMethodNameClassAndSignature(symbolName, nameAndSigArray, bridjNormalPackagePrefix, bridjVersionSpecificPackagePrefix);
062        }
063        
064        static String decodeMethodNameClassAndSignature(String symbolName, Object[] nameAndSigArray, String normalClassPrefix, String replacementClassPrefix) throws NoSuchMethodException, IOException {
065            if (symbolName.startsWith("_"))
066                symbolName = symbolName.substring(1);
067            if (symbolName.startsWith("Java_"))
068                symbolName = symbolName.substring("Java_".length());
069            
070            int i = findLastNonEscapeUnderscore(symbolName);
071            String className = symbolName.substring(0, i).replace('_', '.');
072            if (normalClassPrefix != null) {
073                if (className.startsWith(normalClassPrefix) && !className.startsWith(replacementClassPrefix))
074                    className = replacementClassPrefix + className.substring(normalClassPrefix.length());
075            }
076            String methodName = symbolName.substring(i + 1).replaceAll("_1", "_");
077            
078            NativeMethodsCache mc = getNativeMethodsCache(className.replace('.', '/'));
079            String sig = mc.get(methodName);
080            if (sig == null)
081                throw new NoSuchMethodException("Method " + methodName + " not found in class " + className + " : known method names = " + StringUtils.implode(mc.getNames(), ", "));
082            
083            nameAndSigArray[0] = methodName;
084            nameAndSigArray[1] = sig;
085            
086            String internalClassName = className.replace('.', '/');
087            return internalClassName;//className;
088            
089        }
090            public static String getNativeName(Class c) {
091                    return c.getName().replace('.', '/');   
092            } 
093            public static String getNativeSignature(Method m) {
094            StringBuffer b = new StringBuffer();
095            b.append('(');
096            for (Class c : m.getParameterTypes())
097                b.append(getNativeSignature(c));
098            b.append(')');
099            b.append(getNativeSignature(m.getReturnType()));
100            return b.toString();
101        }
102            public static String getNativeSignature(Class c) {
103                    if (c.isPrimitive()) {
104                if (c == int.class)
105                    return "I";
106                if (c == long.class)
107                    return "J";
108                if (c == short.class)
109                    return "S";
110                if (c == byte.class)
111                    return "B";
112                if (c == boolean.class)
113                    return "Z";
114                if (c == double.class)
115                    return "D";
116                if (c == float.class)
117                    return "F";
118                if (c == char.class)
119                    return "C";
120                if (c == void.class)
121                    return "V";
122                
123                throw new RuntimeException("unexpected case");
124            }
125                    if (c.isArray())
126                            return "[" + getNativeSignature(c.getComponentType());
127                    return "L" + getNativeName(c) + ";";
128            }
129    }