001: /*
002: Copyright (c) 2003-2005, Dennis M. Sosnoski
003: All rights reserved.
004:
005: Redistribution and use in source and binary forms, with or without modification,
006: are permitted provided that the following conditions are met:
007:
008: * Redistributions of source code must retain the above copyright notice, this
009: list of conditions and the following disclaimer.
010: * Redistributions in binary form must reproduce the above copyright notice,
011: this list of conditions and the following disclaimer in the documentation
012: and/or other materials provided with the distribution.
013: * Neither the name of JiBX nor the names of its contributors may be used
014: to endorse or promote products derived from this software without specific
015: prior written permission.
016:
017: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
018: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
019: WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
020: DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
021: ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
022: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
023: LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
024: ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
026: SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027: */
028:
029: package org.jibx.binding.classes;
030:
031: import java.io.File;
032: import java.io.IOException;
033: import java.util.ArrayList;
034: import java.util.HashMap;
035:
036: import org.apache.bcel.classfile.Utility;
037: import org.jibx.runtime.JiBXException;
038:
039: /**
040: * Cache for class files being modified. Handles loading and saving of class
041: * files. Classes are loaded directly from the file system paths supplied on
042: * initialization in preference to the system class path.
043: *
044: * @author Dennis M. Sosnoski
045: * @version 1.0
046: */
047:
048: public class ClassCache {
049: /** Singleton instance of class (created when paths set) */
050: private static ClassCache s_instance;
051:
052: /** Paths to be searched for class files. */
053: private String[] m_paths;
054:
055: /** Root directories corresponding to paths. */
056: private File[] m_roots;
057:
058: /** Map from class names to actual class information. */
059: private HashMap m_classMap;
060:
061: /**
062: * Constructor. Discards jar file paths and normalizes all other paths
063: * (except the empty path) to end with the system path separator character.
064: *
065: * @param paths ordered set of paths to be searched for class files
066: */
067:
068: private ClassCache(String[] paths) {
069: ArrayList keepers = new ArrayList();
070: for (int i = 0; i < paths.length; i++) {
071: File file = new File(paths[i]);
072: if (file.isDirectory() && file.canWrite()) {
073: keepers.add(paths[i]);
074: }
075: }
076: m_paths = new String[keepers.size()];
077: m_roots = new File[keepers.size()];
078: for (int i = 0; i < keepers.size(); i++) {
079: String path = (String) keepers.get(i);
080: int length = path.length();
081: if (length > 0) {
082: if (path.charAt(length - 1) != File.separatorChar) {
083: path = path + File.separator;
084: }
085: }
086: m_paths[i] = path;
087: m_roots[i] = new File(path);
088: }
089: m_classMap = new HashMap();
090: }
091:
092: /**
093: * Get class information. Looks up the class in cache, and if not already
094: * present tries to find it based on the class file search path list. If
095: * the class file is found it is loaded and this method calls itself
096: * recursively to load the whole hierarchy of superclasses.
097: *
098: * @param name fully-qualified name of class to be found
099: * @return class information, or <code>null</code> if class not found
100: * @throws JiBXException on any error accessing class file
101: */
102:
103: private ClassFile getClassFileImpl(String name)
104: throws JiBXException {
105:
106: // first try for match on fully-qualified class name
107: Object match = m_classMap.get(name);
108: if (match != null) {
109: return (ClassFile) match;
110: } else if (ClassItem.isPrimitive(name) || name.endsWith("[]")) {
111:
112: // create synthetic class file for array type
113: ClassFile cf = new ClassFile(name, Utility
114: .getSignature(name));
115: m_classMap.put(name, cf);
116: return cf;
117:
118: } else {
119: try {
120:
121: // got to load it, convert to file path format
122: ClassFile cf = null;
123: String path = name.replace('.', File.separatorChar)
124: + ".class";
125: for (int i = 0; i < m_paths.length; i++) {
126:
127: // check for file found off search path
128: File file = new File(m_paths[i], path);
129: if (file.exists()) {
130:
131: // load file information
132: cf = new ClassFile(name, m_roots[i], file);
133: break;
134:
135: }
136: }
137:
138: // finally try loading (non-modifiable) class from classpath
139: if (cf == null) {
140: cf = new ClassFile(name);
141: }
142:
143: // check for class found
144: if (cf != null) {
145:
146: // force loading of superclass as well (to root)
147: String sname = cf.getSuperName();
148: if (!name.equals(sname) && sname != null) {
149: ClassFile sf = getClassFileImpl(sname);
150: if (sf == null) {
151: throw new JiBXException("Superclass "
152: + sname + " of class " + name
153: + " not found");
154: }
155: cf.setSuperFile(sf);
156: }
157:
158: // add class information to cache
159: m_classMap.put(name, cf);
160:
161: }
162: return cf;
163:
164: } catch (IOException ex) {
165: throw new JiBXException("Error loading class " + name);
166: }
167: }
168: }
169:
170: /**
171: * Get class information. Looks up the class in cache, and if not already
172: * present tries to find it based on the class file search path list. If
173: * the class file is found it is loaded along with all superclasses.
174: *
175: * @param name fully-qualified name of class to be found
176: * @return class information, or <code>null</code> if class not found
177: * @throws JiBXException on any error accessing class file
178: */
179:
180: public static ClassFile getClassFile(String name)
181: throws JiBXException {
182: return s_instance.getClassFileImpl(name);
183: }
184:
185: /**
186: * Check if class information has been loaded.
187: *
188: * @param name full-qualified name of class to be checked
189: * @return <code>true</code> if found
190: */
191: public static boolean hasClassFile(String name) {
192: return s_instance.m_classMap.containsKey(name);
193: }
194:
195: /**
196: * Add created class information to cache.
197: *
198: * @param cf information for class to be added
199: */
200: /*package*/static void addClassFile(ClassFile cf) {
201: s_instance.m_classMap.put(cf.getName(), cf);
202: }
203:
204: /**
205: * Set class paths to be searched. Discards jar file paths and normalizes
206: * all other paths (except the empty path) to end with the system path
207: * separator character.
208: *
209: * @param paths ordered set of paths to be searched for class files
210: */
211:
212: public static void setPaths(String[] paths) {
213: s_instance = new ClassCache(paths);
214: }
215: }
|