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 }