Package org.rococoa

Source Code of org.rococoa.Rococoa$ProxyID

/*
* Copyright 2007, 2008 Duncan McGregor
*
* This file is part of Rococoa, a library to allow Java to talk to Cocoa.
*
* Rococoa is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Rococoa is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Rococoa.  If not, see <http://www.gnu.org/licenses/>.
*/
package org.rococoa;

import java.lang.reflect.Proxy;

import org.rococoa.internal.NSObjectInvocationHandler;
import org.rococoa.internal.OCInvocationCallbacks;
import org.rococoa.internal.VarArgsUnpacker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


import net.sf.cglib.core.DefaultNamingPolicy;
import net.sf.cglib.core.Predicate;
import net.sf.cglib.proxy.Enhancer;

/**
* Static factory for creating Java wrappers for Objective-C instances, and Objective-C
* wrappers for Java instances. <strong>START HERE</strong>.
*
* @author duncan
*
*/
public abstract class Rococoa  {

    private static Logger logging = LoggerFactory.getLogger("org.rococoa.proxy");

    /**
     * Create a Java NSClass representing the Objective-C class with ocClassName
     */
    public static <T extends NSClass> T createClass(String ocClassName, Class<T> type) {
        return wrap(Foundation.getClass(ocClassName), type, false);
    }
   
    /**
     * Create a Java NSObject representing an instance of the Objective-C class
     * ocClassName. The Objective-C instance is created by calling the static
     * factory method named ocMethodName, passing args.
     */
    public static <T extends NSObject> T create(String ocClassName, Class<T> javaClass, String ocMethodName, Object... args) {
        boolean weOwnObject = Foundation.selectorNameMeansWeOwnReturnedObject(ocMethodName);
       
        // If we don't own the object we know that it has been autorelease'd
        // But we need to own these objects, so that they are not dealloc'd when
        // the pool is release'd. So we retain them.
        // Objects that we own (because they were created with 'alloc' or 'new')
        // have not been autorelease'd, so we don't retain them.
        boolean retain = !weOwnObject;
        return create(ocClassName, javaClass, ocMethodName, retain, args);
    }
   
    /**
     * Create a Java NSObject representing an instance of the Objective-C class
     * ocClassName, created with the class method <code>+new</code>.
     */
    public static <T extends NSObject> T create(String ocClassName, Class<T> javaClass) {
        return create(ocClassName, javaClass, "new");
    }

    private static <T extends NSObject> T create(String ocClassName, Class<T> javaClass,
            String ocFactoryName,
            boolean retain,
            Object... args) {
        if (logging.isTraceEnabled()) {
            logging.trace("creating [{} ({})].{}({})",
                    new Object[] {ocClassName, javaClass.getName(), ocFactoryName, new VarArgsUnpacker(args)});
        }

        ID ocClass = Foundation.getClass(ocClassName);
        ID ocInstance = Foundation.send(ocClass, ocFactoryName, ID.class, args);
        checkRetainCount(ocInstance, 1);
        T result = wrap(ocInstance, javaClass, retain);
        checkRetainCount(ocInstance, retain ? 2 : 1);
        return result;
    }
   
    /**
     * Create a Java NSObject wrapping an existing Objective-C instance, represented
     * by id.
     *
     * The NSObject is retained, and released when the object is GC'd.
     */
    public static <T extends NSObject> T wrap(ID id, Class<T> javaClass) {
        return wrap(id, javaClass, true);
    }

    /**
     * Create a Java NSObject down-casting an existing NSObject to a more derived
     * type.
     */
    public static <T extends NSObject> T cast(NSObject object, Class<T> desiredType) {
        return wrap(object.id(), desiredType, true);
    }

    public static <T extends NSObject> T wrap(ID id, Class<T> javaClass, boolean retain) {
        // Why would we not want to retain? Well if we are wrapping a Core Foundation
        // created object, or one created with new (alloc init), it will not
        // have been autorelease'd.
        NSObjectInvocationHandler invocationHandler = new NSObjectInvocationHandler(id, javaClass, retain);
        return createProxy(javaClass, invocationHandler);       
    }
   
    /**
     * Return the ID of a new Objective-C object that will forward messages to
     * javaObject.
     *
     * Keep hold of the ID all the time that methods may be invoked on the Obj-C
     * object, otherwise the callbacks may be GC'd, with amusing consequences.
     *
     * @deprecated because the OC proxy object is never released.
     *  Use {@link Rococoa#proxy} instead.
     */
    @Deprecated
    public static ID wrap(Object javaObject) {
        OCInvocationCallbacks callbacks = new OCInvocationCallbacks(javaObject);
        ID idOfOCProxy = Foundation.newOCProxy(callbacks);
            // idOfOCProxy is owned by us, and we have to release it at some stage
        return new ProxyID(idOfOCProxy, callbacks);
    }
   
    /**
     * Return a new Objective-C object that will forward messages to javaObject,
     * for use in delegates, notifications etc.
     *
     * You need to keep a reference to the returned value for as long as it is
     * active. When it is GC'd, it will release the Objective-C proxy.
     */
    public static NSObject proxy(Object javaObject) {
        return proxy(javaObject, NSObject.class);
    }
   
    public static <T extends NSObject> T proxy(Object javaObject, Class<T> javaType) {
        ID proxyID = wrap(javaObject);
        // we own the proxyID, so by wrapping it as NSObject, we can arrange for
        // it to be release'd when the NSObject is finalized
        return wrap(proxyID, javaType, false);
    }
   
    /**
     * Create a java.lang.reflect.Proxy or cglib proxy of type, which forwards
     * invocations to invococationHandler.
     */
    @SuppressWarnings("unchecked")
    private static <T> T createProxy(final Class<T> type, NSObjectInvocationHandler invocationHandler) {
        if (type.isInterface()) {
            return (T) Proxy.newProxyInstance(
                invocationHandler.getClass().getClassLoader(),
                new Class[] {type}, invocationHandler);
        } else {
            Enhancer e = new Enhancer();
            e.setUseCache(true); // make sure that we reuse if we've already defined
            e.setNamingPolicy(new DefaultNamingPolicy() {
                public String getClassName(String prefix, String source, Object key, Predicate names) {
                    if (source.equals(net.sf.cglib.proxy.Enhancer.class.getName()))
                        return type.getName() + "$$ByRococoa";
                    else
                        return super.getClassName(prefix, source, key, names);
                }});
            e.setSuperclass(type);
            e.setCallback(invocationHandler);
            return (T) e.create();           
        }
    }
   
    // Public only because JNA doesn't call setAccessible to access ctor.
    public static class ProxyID extends ID {
        // used to prevent callbacks being GC'd as long as we hang onto this ID
        @SuppressWarnings("unused")
        private OCInvocationCallbacks callbacks;
       
        public ProxyID() {
            // required by jna
        }
       
        public ProxyID(ID anotherID, OCInvocationCallbacks callbacks) {
            super(anotherID);
            this.callbacks = callbacks;
        }
    }
   
    private static void checkRetainCount(ID ocInstance, int expected) {
        int retainCount = Foundation.cfGetRetainCount(ocInstance);
        if (retainCount != expected)
            throw new IllegalStateException("Created an object which had a retain count of " + retainCount + " not " + expected);
    }

    /**
     * Enforce static factory-ness.
     */
    private Rococoa() {
    }

}
TOP

Related Classes of org.rococoa.Rococoa$ProxyID

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.