001: package org.drools.base;
002:
003: /*
004: * Copyright 2005 JBoss Inc
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */
018:
019: import java.util.ArrayList;
020: import java.util.Collections;
021: import java.util.HashMap;
022: import java.util.HashSet;
023: import java.util.Iterator;
024: import java.util.List;
025: import java.util.Map;
026: import java.util.Set;
027:
028: import org.drools.RuntimeDroolsException;
029:
030: public class ClassTypeResolver implements TypeResolver {
031: private Set imports = Collections.EMPTY_SET;
032:
033: private ClassLoader classLoader;
034:
035: private Map cachedImports = new HashMap();
036:
037: private static final Map internalNamesMap = new HashMap();
038: static {
039: internalNamesMap.put("int", "I");
040: internalNamesMap.put("boolean", "Z");
041: internalNamesMap.put("float", "F");
042: internalNamesMap.put("long", "J");
043: internalNamesMap.put("short", "S");
044: internalNamesMap.put("byte", "B");
045: internalNamesMap.put("double", "D");
046: internalNamesMap.put("char", "C");
047: }
048:
049: public ClassTypeResolver(final Set imports,
050: final ClassLoader classLoader) {
051: this .imports = imports;
052:
053: if (classLoader == null) {
054: throw new RuntimeDroolsException(
055: "ClassTypeResolver cannot have a null parent ClassLoader");
056: }
057:
058: this .classLoader = classLoader;
059: }
060:
061: public void setClassLoader(ClassLoader classLoader) {
062: this .classLoader = classLoader;
063: }
064:
065: /*
066: * (non-Javadoc)
067: *
068: * @see org.drools.semantics.base.Importer#getImports( Class clazz )
069: */
070: /* (non-Javadoc)
071: * @see org.drools.semantics.java.TypeResolver#getImports()
072: */
073: public Set getImports() {
074: return this .imports;
075: }
076:
077: /*
078: * (non-Javadoc)
079: *
080: * @see org.drools.semantics.base.Importer#addImports(org.drools.spi.ImportEntry)
081: */
082: /* (non-Javadoc)
083: * @see org.drools.semantics.java.TypeResolver#addImport(java.lang.String)
084: */
085: public void addImport(final String importEntry) {
086: if (this .imports == Collections.EMPTY_SET) {
087: this .imports = new HashSet();
088: }
089: this .imports.add(importEntry);
090: }
091:
092: public Class lookupFromCache(final String className) {
093: return (Class) this .cachedImports.get(className);
094: }
095:
096: /*
097: * (non-Javadoc)
098: *
099: * @see org.drools.semantics.base.Importer#importClass(java.lang.ClassLoader,
100: * java.lang.String)
101: */
102: /* (non-Javadoc)
103: * @see org.drools.semantics.java.TypeResolver#resolveType(java.lang.String)
104: */
105: public Class resolveType(String className)
106: throws ClassNotFoundException {
107: Class clazz = null;
108: boolean isArray = false;
109: final StringBuffer arrayClassName = new StringBuffer();
110:
111: //is the class a primitive type ?
112: if (internalNamesMap.containsKey(className)) {
113: clazz = Class.forName(
114: "[" + internalNamesMap.get(className), true,
115: this .classLoader).getComponentType();
116: // Could also be a primitive array
117: } else if (className.indexOf('[') > 0) {
118: isArray = true;
119: int bracketIndex = className.indexOf('[');
120: final String componentName = className.substring(0,
121: bracketIndex);
122: arrayClassName.append('[');
123: while ((bracketIndex = className.indexOf('[',
124: bracketIndex + 1)) > 0) {
125: arrayClassName.append('[');
126: }
127: className = componentName;
128: }
129:
130: if (clazz == null) {
131: // Now try the package object type cache
132: clazz = lookupFromCache(className);
133: }
134:
135: // try loading className
136: if (clazz == null) {
137: try {
138: clazz = this .classLoader.loadClass(className);
139: } catch (final ClassNotFoundException e) {
140: clazz = null;
141: }
142: }
143:
144: // Now try the className with each of the given imports
145: if (clazz == null) {
146: final Set validClazzCandidates = new HashSet();
147:
148: final Iterator it = this .imports.iterator();
149: while (it.hasNext()) {
150: clazz = importClass((String) it.next(), className);
151: if (clazz != null) {
152: validClazzCandidates.add(clazz);
153: }
154: }
155:
156: // If there are more than one possible resolutions, complain about
157: // the ambiguity
158: if (validClazzCandidates.size() > 1) {
159: final StringBuffer sb = new StringBuffer();
160: final Iterator clazzCandIter = validClazzCandidates
161: .iterator();
162: while (clazzCandIter.hasNext()) {
163: if (0 != sb.length()) {
164: sb.append(", ");
165: }
166: sb.append(((Class) clazzCandIter.next()).getName());
167: }
168: throw new Error(
169: "Unable to find unambiguously defined class '"
170: + className + "', candidates are: ["
171: + sb.toString() + "]");
172: } else if (validClazzCandidates.size() == 1) {
173: clazz = (Class) validClazzCandidates.toArray()[0];
174: } else {
175: clazz = null;
176: }
177:
178: }
179:
180: // Now try the java.lang package
181: if (clazz == null) {
182: clazz = defaultClass(className);
183: }
184:
185: // If array component class was found, try to resolve the array class of it
186: if (isArray) {
187: if (clazz == null
188: && internalNamesMap.containsKey(className)) {
189: arrayClassName.append(internalNamesMap.get(className));
190: } else {
191: if (clazz != null) {
192: arrayClassName.append("L").append(clazz.getName())
193: .append(";");
194: } else {
195: // we know we will probably not be able to resolve this name, but nothing else we can do.
196: arrayClassName.append("L").append(className)
197: .append(";");
198: }
199: }
200: try {
201: clazz = Class.forName(arrayClassName.toString());
202: } catch (final ClassNotFoundException e) {
203: clazz = null;
204: }
205: }
206:
207: // We still can't find the class so throw an exception
208: if (clazz == null) {
209: throw new ClassNotFoundException("Unable to find class '"
210: + className + "'");
211: }
212:
213: return clazz;
214: }
215:
216: private Class importClass(final String importText,
217: final String className) {
218: String qualifiedClass = null;
219: Class clazz = null;
220:
221: if (importText.endsWith("*")) {
222: qualifiedClass = importText.substring(0, importText
223: .indexOf('*'))
224: + className;
225: } else if (importText.endsWith("." + className)) {
226: qualifiedClass = importText;
227: } else if (importText.equals(className)) {
228: qualifiedClass = importText;
229: }
230:
231: if (qualifiedClass != null) {
232: try {
233: clazz = this .classLoader.loadClass(qualifiedClass);
234: } catch (final ClassNotFoundException e) {
235: clazz = null;
236: }
237:
238: // maybe its a nested class?
239: if (clazz == null) {
240: try {
241: final int lastIndex = qualifiedClass
242: .lastIndexOf('.');
243: qualifiedClass = qualifiedClass.substring(0,
244: lastIndex)
245: + "$"
246: + qualifiedClass.substring(lastIndex + 1);
247: clazz = this .classLoader.loadClass(qualifiedClass);
248: } catch (final ClassNotFoundException e) {
249: clazz = null;
250: }
251: }
252: }
253:
254: if (clazz != null) {
255: if (this .cachedImports == Collections.EMPTY_MAP) {
256: this .cachedImports = new HashMap();
257: }
258:
259: this .cachedImports.put(className, clazz);
260: }
261:
262: return clazz;
263: }
264:
265: private Class defaultClass(final String className) {
266: final String qualifiedClass = "java.lang." + className;
267: Class clazz = null;
268: try {
269: clazz = this .classLoader.loadClass(qualifiedClass);
270: } catch (final ClassNotFoundException e) {
271: // do nothing
272: }
273: if (clazz != null) {
274: if (this .cachedImports == Collections.EMPTY_MAP) {
275: this .cachedImports = new HashMap();
276: }
277: this .cachedImports.put(className, clazz);
278: }
279: return clazz;
280: }
281:
282: public boolean isEmpty() {
283: return this.imports.isEmpty();
284: }
285: }
|