public T build()

in tapestry-clojure/src/main/java/org/apache/tapestry5/internal/clojure/ClojureBuilderImpl.java [47:150]


    public <T> T build(final Class<T> interfaceType)
    {
        assert interfaceType != null;
        assert interfaceType.isInterface();

        Namespace annotation = interfaceType.getAnnotation(Namespace.class);

        if (annotation == null)
        {
            throw new IllegalArgumentException(String.format("Interface type %s does not have the Namespace annotation.",
                    interfaceType.getName()));
        }

        final String namespace = annotation.value();

        ClassInstantiator<T> instantiator = proxyFactory.createProxy(interfaceType, new PlasticClassTransformer()
        {
            @Override
            public void transform(PlasticClass plasticClass)
            {
                for (final Method m : interfaceType.getMethods())
                {
                    bridgeToClojure(plasticClass, m);
                }
            }

            private void bridgeToClojure(final PlasticClass plasticClass, final Method method)
            {
                final MethodDescription desc = new MethodDescription(method);

                if (method.getReturnType() == void.class)
                {
                    throw new IllegalArgumentException(String.format("Method %s may not be void when bridging to Clojure functions.",
                            desc));
                }

                final Symbol symbol = mapper.mapMethod(namespace, method);

                tracker.run(String.format("Mapping %s method %s to Clojure function %s",
                        interfaceType.getName(),
                        desc.toShortString(),
                        symbol.toString()), new Runnable()
                {
                    @Override
                    public void run()
                    {

                        Symbol namespaceSymbol = Symbol.create(symbol.getNamespace());

                        REQUIRE.invoke(namespaceSymbol);

                        IFn clojureFunction = Clojure.var(symbol);

                        final PlasticField fnField = plasticClass.introduceField(IFn.class, method.getName() + "IFn").inject(clojureFunction);

                        plasticClass.introduceMethod(desc).changeImplementation(new InstructionBuilderCallback()
                        {
                            @Override
                            public void doBuild(InstructionBuilder builder)
                            {
                                bridgeToClojure(builder, desc, fnField);
                            }
                        });

                    }
                });

            }

            private void bridgeToClojure(InstructionBuilder builder, MethodDescription description, PlasticField ifnField)
            {
                builder.loadThis().getField(ifnField);

                int count = description.argumentTypes.length;

                Class[] invokeParameterTypes = new Class[count];

                for (int i = 0; i < count; i++)
                {
                    invokeParameterTypes[i] = Object.class;

                    builder.loadArgument(i).boxPrimitive(description.argumentTypes[i]);
                }

                Method ifnMethod = null;

                try
                {
                    ifnMethod = IFn.class.getMethod("invoke", invokeParameterTypes);
                } catch (NoSuchMethodException ex)
                {
                    throw new RuntimeException(String.format("Unable to find correct IFn.invoke() method: %s",
                            ExceptionUtils.toMessage(ex)), ex);
                }

                builder.invoke(ifnMethod);

                builder.castOrUnbox(description.returnType);
                builder.returnResult();
            }
        });

        return instantiator.newInstance();
    }