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