001    /*****************************************************************************
002     * Copyright (C) NanoContainer Organization. All rights reserved.            *
003     * ------------------------------------------------------------------------- *
004     * The software in this package is published under the terms of the BSD      *
005     * style license a copy of which has been included with this distribution in *
006     * the LICENSE.txt file.                                                     *
007     * Original code by Nick Sieger                                                                          *
008     *****************************************************************************/
009    
010    package org.nanocontainer.script.jruby;
011    
012    import java.io.IOException;
013    import java.io.Reader;
014    import java.io.StringWriter;
015    import java.util.Collections;
016    
017    import org.jruby.Ruby;
018    import org.jruby.exceptions.RaiseException;
019    import org.jruby.javasupport.JavaEmbedUtils;
020    import org.jruby.runtime.builtin.IRubyObject;
021    import org.nanocontainer.integrationkit.PicoCompositionException;
022    import org.nanocontainer.reflection.DefaultNanoPicoContainer;
023    import org.nanocontainer.script.NanoContainerMarkupException;
024    import org.nanocontainer.script.ScriptedContainerBuilder;
025    import org.picocontainer.PicoContainer;
026    import org.picocontainer.alternatives.EmptyPicoContainer;
027    
028    /**
029     * The script uses the {@code nanocontainer.rb} script to create an instance of
030     * {@link PicoContainer}.
031     * There are implicit variables named "$parent" and "$assembly_scope".
032     *
033     * @author Nick Sieger
034     */
035    public class JRubyContainerBuilder extends ScriptedContainerBuilder {
036        public static final String MARKUP_EXCEPTION_PREFIX = "nanobuilder: ";
037    
038        private String script;
039    
040        public JRubyContainerBuilder(Reader script, ClassLoader classLoader) {
041            super(script, classLoader);
042            this.script = toString( script );
043        }
044    
045        private String toString(Reader script) {
046            int charsRead;
047            char[] chars = new char[1024];
048            StringWriter writer = new StringWriter();
049            try {
050                while ((charsRead = script.read(chars)) != -1) {
051                    writer.write(chars, 0, charsRead);
052                }
053            } catch (IOException e) {
054                throw new RuntimeException("unable to read script from reader", e);
055            }
056            return writer.toString();
057        }
058    
059        protected PicoContainer createContainerFromScript(PicoContainer parentContainer, Object assemblyScope) {
060            if (parentContainer == null) {
061                parentContainer = new EmptyPicoContainer();
062            }
063            parentContainer = new DefaultNanoPicoContainer(getClassLoader(), parentContainer);
064    
065            Ruby ruby = JavaEmbedUtils.initialize(Collections.EMPTY_LIST);
066            ruby.getLoadService().require("org/nanocontainer/script/jruby/nanobuilder");
067            ruby.defineReadonlyVariable("$parent", JavaEmbedUtils.javaToRuby(ruby, parentContainer));
068            ruby.defineReadonlyVariable("$assembly_scope", JavaEmbedUtils.javaToRuby(ruby, assemblyScope));
069            try {
070                IRubyObject result = ruby.evalScript(script);
071                return (PicoContainer) JavaEmbedUtils.rubyToJava(ruby, result, PicoContainer.class);
072            } catch (RaiseException re) {
073                if (re.getCause() instanceof NanoContainerMarkupException) {
074                    throw (NanoContainerMarkupException) re.getCause();
075                }
076                String message = (String) JavaEmbedUtils.rubyToJava(ruby, re.getException().message, String.class);
077                if (message.startsWith(MARKUP_EXCEPTION_PREFIX)) {
078                    throw new NanoContainerMarkupException(message.substring(MARKUP_EXCEPTION_PREFIX.length()));
079                } else {
080                    throw new PicoCompositionException(message, re);
081                }
082            }
083        }
084    }