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     *                                                                           *
008     *****************************************************************************/
009    
010    package org.nanocontainer.script;
011    
012    import java.io.File;
013    import java.net.URL;
014    import java.util.HashMap;
015    import java.util.Map;
016    
017    /**
018     * ScriptBuilderResolver handles the task of resolving a file name to a builder
019     * name.  Typical default resolution is for Groovy, BeanShell, JavaScript,
020     * Jython, and XML script names.  However, you can register/replace your
021     * own builder implementations by using the registerBuilder() function.
022     * @author Michael Rimov
023     */
024    public class ScriptBuilderResolver {
025    
026        public static final String GROOVY = ".groovy";
027        public static final String BEANSHELL = ".bsh";
028        public static final String JAVASCRIPT = ".js";
029        public static final String JYTHON = ".py";
030        public static final String XML = ".xml";
031    
032        public static final String DEFAULT_GROOVY_BUILDER = "org.nanocontainer.script.groovy.GroovyContainerBuilder";
033        public static final String DEFAULT_BEANSHELL_BUILDER = "org.nanocontainer.script.bsh.BeanShellContainerBuilder";
034        public static final String DEFAULT_JAVASCRIPT_BUILDER = "org.nanocontainer.script.rhino.JavascriptContainerBuilder";
035        public static final String DEFAULT_XML_BUILDER = "org.nanocontainer.script.xml.XMLContainerBuilder";
036        public static final String DEFAULT_JYTHON_BUILDER = "org.nanocontainer.script.jython.JythonContainerBuilder";
037    
038        private final Map extensionToBuilders = new HashMap();
039    
040    
041    
042        public ScriptBuilderResolver() {
043            resetBuilders();
044        }
045    
046    
047        /**
048         * Retrieve the classname of the appropriate ScriptedContainerBuilder given the file.
049         * @param compositionFile File
050         * @return String
051         */
052        public String getBuilderClassName(File compositionFile) {
053            String language = getExtension(compositionFile.getAbsolutePath());
054            return getBuilderClassName(language);
055        }
056    
057    
058    
059    
060        /**
061         * Retrieve the classname of the appropriate ScriptedContainerBuilder given the file.
062         * @param compositionFile File
063         * @return String
064         */
065        public String getBuilderClassName(URL compositionURL) {
066            String language = getExtension(compositionURL.getFile());
067            return getBuilderClassName(language);
068        }
069    
070        /**
071         * Retrieve the classname of the builder to use given the provided
072         * extension.  Example:
073         * <code><pre>
074         * ScriptedContainerBuilderFactory factory = new ScriptedContainerBuilderFactory(.....);
075         * String groovyBuilderName = factory.getBuilderClassName(&quot;.groovy&quot;);
076         * assert &quot;org.nanocontainer.script.groovy.GroovyContainerBuilder&quot;.equals(groovyBuilderName);
077         * </pre></code>
078         * @param extension String
079         * @return String
080         */
081        public synchronized String getBuilderClassName(final String extension) throws UnsupportedScriptTypeException{
082            String resultingBuilderClassName = null;
083            resultingBuilderClassName = (String) extensionToBuilders.get(extension);
084            if (resultingBuilderClassName == null) {
085                throw new UnsupportedScriptTypeException(extension, this.getAllSupportedExtensions());
086            }
087            return resultingBuilderClassName;
088        }
089    
090        /**
091         * Function to allow the resetting of the builder map to defaults.  Allows
092         * testing of the static resource a bit better.
093         */
094        public synchronized void resetBuilders() {
095            extensionToBuilders.clear();
096    
097            //This is a bit clunky compared to just registering the items
098            //directly into the map, but this way IMO it provides a single access
099            //point into the extensionToBuilders map.
100            registerBuilder(GROOVY, DEFAULT_GROOVY_BUILDER);
101            registerBuilder(BEANSHELL, DEFAULT_BEANSHELL_BUILDER);
102            registerBuilder(JAVASCRIPT, DEFAULT_JAVASCRIPT_BUILDER);
103            registerBuilder(XML, DEFAULT_XML_BUILDER);
104            registerBuilder(JYTHON, DEFAULT_JYTHON_BUILDER);
105    
106        }
107    
108        /**
109         * Registers/replaces a new handler for a given extension.  Allows for customizable
110         * behavior in the various builders or the possibility to dynamically add
111         * handlers for new file types.  Example:
112         * <code><pre>
113         * ScriptedContainerBuilderFactory factory = new ScriptedContainerBuilderFactory(...)
114         * factory.registerBuilder(&quot;.groovy&quot;, &quot;org.nanocontainer.script.groovy.GroovyContainerBuilder&quot;);
115         * ScriptedContainerBuilder builder = factory.getContainerBuilder();
116         * assertNotNull(builder);
117         * </pre></code>
118         * <p>The internal code now requires synchronization of the builder extension map since
119         * who knows what is using it when a new builder is registered.</p>
120         * @param extension String the extension to register under.
121         * @param className String the classname to use for the given extension.
122         */
123        public synchronized void registerBuilder(final String extension, final String className) {
124            extensionToBuilders.put(extension, className);
125        }
126    
127        /**
128         * Retrieve a list of all supported extensions.
129         * @return String[] of extensions including the period in the name.
130         */
131        public synchronized String[] getAllSupportedExtensions() {
132             return (String[]) extensionToBuilders.keySet().toArray(new String[extensionToBuilders.size()]);
133        }
134    
135    
136        /**
137         * Retrieve the extension of the file name.
138         * @param fileName String
139         * @return String
140         */
141        private String getExtension(String fileName) {
142            return fileName.substring(fileName.lastIndexOf("."));
143        }
144    
145    
146    }