1 /*
   2  * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package com.apple.jobjc;
  26 
  27 import java.lang.ref.WeakReference;
  28 import java.lang.reflect.Constructor;
  29 import java.util.LinkedHashMap;
  30 import java.util.Map;
  31 
  32 import javax.tools.annotation.GenerateNativeHeader;
  33 
  34 /* No native methods here, but the constants are needed in the supporting JNI code */
  35 @GenerateNativeHeader
  36 public class ID extends Pointer<Void>{
  37     static native String getNativeDescription(final long objPtr);
  38 
  39     final JObjCRuntime runtime;
  40 
  41     static final Class[] CTOR_ARGS = { long.class, JObjCRuntime.class };
  42     protected ID(final long objPtr, final JObjCRuntime runtime) {
  43         super(objPtr);
  44         runtime.assertOK();
  45         this.runtime = runtime;
  46     }
  47 
  48     protected ID(final ID obj, final JObjCRuntime runtime) {
  49         this(obj.ptr, runtime);
  50     }
  51 
  52     @Override protected NativeObjectLifecycleManager getNativeObjectLifecycleManager() {
  53         return NativeObjectLifecycleManager.CFRetainRelease.INST;
  54     }
  55 
  56     protected final JObjCRuntime getRuntime() { return runtime; }
  57 
  58     @Override public String toString(){
  59         String s = super.toString();
  60         return s + " (ObjC: " + ptr + " / " + Long.toHexString(ptr) + ")";
  61     }
  62 
  63     //
  64 
  65     public static <T extends ID> T getInstance(final long ptr, final JObjCRuntime runtime){
  66         return (T) getObjCObjectFor(runtime, ptr);
  67     }
  68 
  69     static <T extends ID> T getObjCObjectFor(final JObjCRuntime runtime, final long objPtr){
  70         if (objPtr == 0) return null;
  71 
  72         final WeakReference cachedObj = objectCache.get().get(objPtr);
  73         if(cachedObj != null && cachedObj.get() != null) return (T) cachedObj.get();
  74 
  75         final long clsPtr = NSClass.getClass(objPtr);
  76 
  77         final T newObj = (T) (runtime.subclassing.isUserClass(clsPtr) ?
  78                 Subclassing.getJObjectFromIVar(objPtr)
  79                 : createNewObjCObjectFor(runtime, objPtr, clsPtr));
  80 
  81         objectCache.get().put(objPtr, new WeakReference(newObj));
  82         return newObj;
  83     }
  84 
  85     static <T extends ID> T createNewObjCObjectFor(final JObjCRuntime runtime, final long objPtr, final long clsPtr) {
  86         final Constructor<T> ctor = getConstructorForClassPtr(runtime, clsPtr);
  87         return (T) createNewObjCObjectForConstructor(ctor, objPtr, runtime);
  88     }
  89 
  90     @SuppressWarnings("unchecked")
  91     static <T extends ID> Constructor<T> getConstructorForClassPtr(final JObjCRuntime runtime, final long clazzPtr){
  92         final Constructor<T> cachedCtor = (Constructor<T>) constructorCache.get().get(clazzPtr);
  93         if(cachedCtor != null) return cachedCtor;
  94 
  95         final Class<T> clazz = getClassForClassPtr(runtime, clazzPtr);
  96         Constructor<T> ctor;
  97         try {
  98             ctor = clazz.getDeclaredConstructor(CTOR_ARGS);
  99         } catch (SecurityException e) {
 100             throw new RuntimeException(e);
 101         } catch (NoSuchMethodException e) {
 102             throw new RuntimeException(e);
 103         }
 104         ctor.setAccessible(true);
 105         constructorCache.get().put(clazzPtr, (Constructor<ID>) ctor);
 106         return ctor;
 107     }
 108 
 109     @SuppressWarnings("unchecked")
 110     static <T extends ID> Class<T> getClassForClassPtr(final JObjCRuntime runtime, final long clazzPtr){
 111         final String className = NSClass.getClassNameOfClass(clazzPtr);
 112         final Class<T> clazz = (Class<T>) runtime.getClassForNativeClassName(className);
 113         if(clazz == null){
 114             final long superClazzPtr = NSClass.getSuperClassOfClass(clazzPtr);
 115             if(superClazzPtr != 0)
 116                 return getClassForClassPtr(runtime, superClazzPtr);
 117         }
 118         return clazz;
 119     }
 120 
 121     static <T extends ID> T createNewObjCObjectForConstructor(final Constructor ctor, final long objPtr, final JObjCRuntime runtime) {
 122         try {
 123             final T newInstance = (T) ctor.newInstance(new Object[] { Long.valueOf(objPtr), runtime });
 124             objectCache.get().put(objPtr, new WeakReference(newInstance));
 125             return newInstance;
 126         } catch (final Exception e) {
 127             throw new RuntimeException(e);
 128         }
 129     }
 130 
 131     static <T extends ID> T createNewObjCObjectForClass(final Class<T> clazz, final long objPtr, final JObjCRuntime runtime) {
 132         try {
 133             final Constructor<T> constructor = clazz.getDeclaredConstructor(CTOR_ARGS);
 134             constructor.setAccessible(true);
 135             return (T) createNewObjCObjectForConstructor(constructor, objPtr, runtime);
 136         } catch (final Exception e) {
 137             throw new RuntimeException(e);
 138         }
 139     }
 140 
 141     //
 142 
 143     static final ThreadLocal<LinkedHashMap<Long, Constructor>> constructorCache = new ThreadLocal<LinkedHashMap<Long, Constructor>>(){
 144         @Override protected LinkedHashMap<Long, Constructor> initialValue(){
 145             final int MAX_ENTRIES = 1000;
 146             final float LOAD_FACTOR = 0.75f;
 147             return new LinkedHashMap<Long, Constructor>((int) (MAX_ENTRIES/LOAD_FACTOR), LOAD_FACTOR, true) {
 148                 @Override protected boolean removeEldestEntry(Map.Entry<Long, Constructor> eldest) {
 149                     return size() > MAX_ENTRIES;
 150                 }
 151             };
 152         }
 153     };
 154 
 155     static final ThreadLocal<LinkedHashMap<Long, WeakReference>> objectCache = new ThreadLocal<LinkedHashMap<Long, WeakReference>>(){
 156         @Override protected LinkedHashMap<Long, WeakReference> initialValue(){
 157             final int MAX_ENTRIES = 1000;
 158             final float LOAD_FACTOR = 0.75f;
 159             return new LinkedHashMap<Long, WeakReference>((int) (MAX_ENTRIES/LOAD_FACTOR), LOAD_FACTOR, true) {
 160                 @Override protected boolean removeEldestEntry(Map.Entry<Long, WeakReference> eldest) {
 161                     return size() > MAX_ENTRIES || eldest.getValue().get() == null;
 162                 }
 163             };
 164         }
 165     };
 166 }