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(".groovy");
076 * assert "org.nanocontainer.script.groovy.GroovyContainerBuilder".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(".groovy", "org.nanocontainer.script.groovy.GroovyContainerBuilder");
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 }