001/* 002 * Copyright 2002-2014 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.expression.spel.support; 018 019import java.util.Collections; 020import java.util.LinkedList; 021import java.util.List; 022 023import org.springframework.expression.EvaluationException; 024import org.springframework.expression.TypeLocator; 025import org.springframework.expression.spel.SpelEvaluationException; 026import org.springframework.expression.spel.SpelMessage; 027import org.springframework.util.ClassUtils; 028 029/** 030 * A simple implementation of {@link TypeLocator} that uses the context ClassLoader 031 * (or any ClassLoader set upon it). It supports 'well-known' packages: So if a 032 * type cannot be found, it will try the registered imports to locate it. 033 * 034 * @author Andy Clement 035 * @author Juergen Hoeller 036 * @since 3.0 037 */ 038public class StandardTypeLocator implements TypeLocator { 039 040 private final ClassLoader classLoader; 041 042 private final List<String> knownPackagePrefixes = new LinkedList<String>(); 043 044 045 /** 046 * Create a StandardTypeLocator for the default ClassLoader 047 * (typically, the thread context ClassLoader). 048 */ 049 public StandardTypeLocator() { 050 this(ClassUtils.getDefaultClassLoader()); 051 } 052 053 /** 054 * Create a StandardTypeLocator for the given ClassLoader. 055 * @param classLoader the ClassLoader to delegate to 056 */ 057 public StandardTypeLocator(ClassLoader classLoader) { 058 this.classLoader = classLoader; 059 // Similar to when writing regular Java code, it only knows about java.lang by default 060 registerImport("java.lang"); 061 } 062 063 064 /** 065 * Register a new import prefix that will be used when searching for unqualified types. 066 * Expected format is something like "java.lang". 067 * @param prefix the prefix to register 068 */ 069 public void registerImport(String prefix) { 070 this.knownPackagePrefixes.add(prefix); 071 } 072 073 /** 074 * Remove that specified prefix from this locator's list of imports. 075 * @param prefix the prefix to remove 076 */ 077 public void removeImport(String prefix) { 078 this.knownPackagePrefixes.remove(prefix); 079 } 080 081 /** 082 * Return a list of all the import prefixes registered with this StandardTypeLocator. 083 * @return a list of registered import prefixes 084 */ 085 public List<String> getImportPrefixes() { 086 return Collections.unmodifiableList(this.knownPackagePrefixes); 087 } 088 089 090 /** 091 * Find a (possibly unqualified) type reference - first using the type name as-is, 092 * then trying any registered prefixes if the type name cannot be found. 093 * @param typeName the type to locate 094 * @return the class object for the type 095 * @throws EvaluationException if the type cannot be found 096 */ 097 @Override 098 public Class<?> findType(String typeName) throws EvaluationException { 099 String nameToLookup = typeName; 100 try { 101 return ClassUtils.forName(nameToLookup, this.classLoader); 102 } 103 catch (ClassNotFoundException ey) { 104 // try any registered prefixes before giving up 105 } 106 for (String prefix : this.knownPackagePrefixes) { 107 try { 108 nameToLookup = prefix + '.' + typeName; 109 return ClassUtils.forName(nameToLookup, this.classLoader); 110 } 111 catch (ClassNotFoundException ex) { 112 // might be a different prefix 113 } 114 } 115 throw new SpelEvaluationException(SpelMessage.TYPE_NOT_FOUND, typeName); 116 } 117 118}