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 }