View Javadoc

1   /*
2    Wotonomy: OpenStep design patterns for pure Java applications.
3    Copyright (C) 2005 Israfil Consulting Services Corporation
4    Copyright (C) 2005 Christian Gruber
5   
6    This library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10  
11   This library is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   Lesser General Public License for more details.
15  
16   You should have received a copy of the GNU Lesser General Public
17   License along with this library; if not, see http://www.gnu.org
18   */
19  package net.wotonomy.foundation;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.io.PrintWriter;
25  import java.io.StringWriter;
26  import java.net.MalformedURLException;
27  import java.net.URL;
28  import java.util.Locale;
29  import java.util.Properties;
30  import java.util.jar.JarEntry;
31  import java.util.jar.JarFile;
32  import java.util.jar.JarInputStream;
33  
34  import net.wotonomy.foundation.internal.NetworkClassLoader;
35  
36  /***
37   * An implementation of NSBundle. Unlike the standard WebObjects NSBundle, this
38   * implementation loads bundles dynamically. This means that all bundles do not
39   * need to exist in the classpath at startup time. Practically, this means that
40   * NSBundle has a custom classloader.
41   * 
42   * This behaviour is not supported in Apple's WebObjects, and should be used
43   * only if compatibility is not desired. It is largely intended for internal use
44   * within this framework, but is exposed through bundleForURL()
45   * 
46   * Another difference between Wotonomy's NSBundle and Apple's implementation is
47   * the ability to initialize the application with a custom resource lookup
48   * "path".
49   * 
50   * @author cgruber@israfil.net
51   * @author $Author: cgruber $
52   * @version $Revision: 892 $
53   * 
54   */
55  public class NSBundle {
56  
57  	/* Class variables */
58  
59  	public static final String BundleDidLoadNotification = "NSBundleDidLoadNotification";
60  
61  	public static final String LoadedClassesNotification = "NSLoadedClassesNotification";
62  
63  	private static final NSMutableArray _allBundles = new NSMutableArray();
64  
65  	private static final NSMutableArray _allFrameworks = new NSMutableArray();
66  
67  	private static NSMutableDictionary _languageCodes = new NSMutableDictionary();
68  
69  	private static NSBundle _mainBundle = null;
70  	
71  	protected static NetworkClassLoader _classLoader = new NetworkClassLoader(ClassLoader.getSystemClassLoader());
72  
73  	/* Instance variables */
74  
75  	protected String name;
76  
77  	protected NSMutableDictionary info = null;
78  
79  	protected String path;
80  
81  	protected NSMutableArray classNames = new NSMutableArray();
82  
83  	protected NSMutableArray packages = new NSMutableArray();
84  
85  	protected Properties properties;
86  
87  	protected boolean isFramework = false;
88  
89  	protected boolean loaded = false;
90  
91  	protected Class principalClass;
92  
93  	/* Constructors */
94  
95  	/***
96  	 * The default constructor, which is only public to support other framework
97  	 * functionality, and to be API compatible with Apple's WebObjects.
98  	 * Generally, framework users should use bundleForXXXX() methods.
99  	 */
100 	public NSBundle() {
101 	}
102 
103 	/* Methods */
104 
105 	/***
106 	 * @deprecated use mainBundle() to access the application bundle and
107 	 *             frameworkBundles() to access any frameworks.
108 	 */
109 	public static synchronized NSArray allBundles() {
110 		return _allBundles.immutableClone();
111 	}
112 
113 	/***
114 	 * @deprecated use frameworkBundles() to access any frameworks.
115 	 */
116 	public static NSArray allFrameworks() {
117 		return frameworkBundles();
118 	}
119 
120 	/***
121 	 * Returns the bundle that contains the provided class, if any. Otherwise,
122 	 * it returns null. Because NSBundles have a specialized class-loader, if
123 	 * any two bundles contain duiplicates of the same class, the second will
124 	 * fail to load. TODO: Determine if class-load scoping of duplicate classes
125 	 * is appropriate.
126 	 * 
127 	 * @param class1
128 	 * @return NSBundle
129 	 */
130 	public static synchronized NSBundle bundleForClass(Class class1) {
131 		throw new UnsupportedOperationException("Method not yet implemented.");
132 		// TODO: Implement.
133 	}
134 
135 	/***
136 	 * @deprecated Apple's WebObjects says you should not load from arbitrary
137 	 *             path.
138 	 * @param path
139 	 * @return
140 	 */
141 	public static synchronized NSBundle bundleWithPath(String path) {
142 		try {
143 			return bundleWithURL(new File(path).toURI().toURL());
144 		} catch (MalformedURLException e) {
145 			NSLog.err.appendln("Bundle path is invalid: " + path);
146 			return null;
147 		}
148 	}
149 
150 	/***
151 	 * <strong>Note:</strong>This method is only in Wotonomy.
152 	 * 
153 	 * This method returns a bundle at a given URL, registering that bundle as
154 	 * well. If the bundle has already been loaded/registered, it is simply
155 	 * returned from the cache.
156 	 * 
157 	 * @param url
158 	 * @return
159 	 */
160 	public static synchronized NSBundle bundleWithURL(URL url) {
161 		NSBundle result = null;
162 		String sep = System.getProperty("file.separator");
163 		String protocol = url.getProtocol();
164 		if (protocol.equals("file")) {
165 			File f = new File(url.getPath());
166 			if (!f.exists()) {
167 				NSLog.err.appendln("Bundle not found: " + url);
168 				return null;
169 			}
170 			StringBuffer filename = new StringBuffer(f.getName());
171 			int extensionIndex = filename.lastIndexOf(".");
172 			if (extensionIndex == -1) {
173 				NSLog.err
174 						.appendln("Named URL does not point to a bundle with an extension: "
175 								+ url);
176 				return null;
177 			}
178 			String basename = filename.substring(0, extensionIndex);
179 			String extension = filename.substring(extensionIndex + 1, filename
180 					.length());
181 			System.out.println("basename: " + basename);
182 			System.out.println("extension: " + extension);
183 			result = new NSBundle();
184 			result.name = basename;
185 			result.isFramework = extension.equals("framework");
186 			if (f.isDirectory()) {
187 				try {
188 					File javadir = new File(f.getCanonicalPath() + sep + "Contents"
189 							+ sep + "Resources" + sep + "Java");
190 					System.out.println(javadir);
191 					System.out.println(javadir.exists());
192 					File[] jars = javadir.listFiles();
193 					
194 				} catch (IOException e) { }
195 			} else {
196 				throw new RuntimeException(
197 						"Compressed bundle files not currently supported.");
198 			}
199 			throw new RuntimeException("Method not finished.");
200 		} else {
201 			try {
202 				JarInputStream j = new JarInputStream(url.openStream());
203 				JarEntry entry1 = j.getNextJarEntry();
204 
205 				JarFile f;
206 				throw new RuntimeException("Method not finished.");
207 			} catch (IOException e) {
208 				NSLog.err
209 						.appendln("IOException loading framework jar from URL "
210 								+ url + " - message: "
211 								+ e.getLocalizedMessage());
212 				StringWriter stacktrace = new StringWriter();
213 				e.printStackTrace(new PrintWriter(stacktrace));
214 				NSLog.err.appendln(stacktrace);
215 				return null;
216 			}
217 		}
218 	}
219 
220 	/***
221 	 * This method returns a bundle, either from cache, or if it doesn't exist
222 	 * yet, it attempts to look it up - first from the classpath, then from the
223 	 * resource path. TODO: Determine if the lookup order is the desired
224 	 * semantic.
225 	 * 
226 	 * @param name
227 	 * @return
228 	 */
229 	public static synchronized NSBundle bundleForName(String name) {
230 		throw new UnsupportedOperationException("Method not yet implemented.");
231 		// TODO: Implement.
232 	}
233 
234 	public static synchronized NSArray frameworkBundles() {
235 		return _allFrameworks.immutableClone();
236 	}
237 
238 	/***
239 	 * Used to set the "Main" application bundle, in which primary resources are
240 	 * loaded for GUI applications. This is mostly only relevant for
241 	 * XXApplication objects. This should therefore not be generally used by
242 	 * consumers of the framework.
243 	 * 
244 	 * @param aBundle
245 	 */
246 	public static void setMainBundle(NSBundle aBundle) {
247 		_mainBundle = aBundle;
248 	}
249 
250 	public static NSBundle mainBundle() {
251 		return _mainBundle;
252 	}
253 
254 	/***
255 	 * Get the default prefix for locale. TODO: This really needs to be made
256 	 * dynamic somehow.
257 	 * 
258 	 * @return
259 	 */
260 	protected static String defaultLocalePrefix() {
261 		String language = (String) _languageCodes.objectForKey(Locale
262 				.getDefault().getLanguage());
263 		return language + ".lproj";
264 	}
265 
266 	protected static synchronized NSBundle findOrCreateBundleWithPath(String s) {
267 		throw new UnsupportedOperationException("Method not yet implemented.");
268 		// TODO: Implement.
269 	}
270 
271 	/***
272 	 * TODO: figure out what this does.
273 	 * 
274 	 * @return
275 	 */
276 	public NSArray bundleClassPackageNames() {
277 		throw new UnsupportedOperationException("Method not yet implemented.");
278 		// TODO: Implement.
279 	}
280 
281 	public String bundlePath() {
282 		return this.path;
283 	}
284 
285 	/***
286 	 * Returns a byte array for the given resource path. TODO: Lookup semantics
287 	 * in WebObjects javadocs.
288 	 * 
289 	 * @param path
290 	 * @return
291 	 */
292 	public byte[] bytesForResourcePath(String path) {
293 		throw new UnsupportedOperationException("Method not yet implemented.");
294 		// TODO: Implement.
295 	}
296 
297 	public NSArray bundleClassNames() {
298 		return classNames.immutableClone();
299 	}
300 
301 	public NSDictionary infoDictionary() {
302 		return info;
303 	}
304 
305 	/***
306 	 * Returns an input stream for a given resource path. TODO: Lookup semantics
307 	 * in WebObjects javadocs.
308 	 * 
309 	 * @param path
310 	 * @return
311 	 */
312 	public InputStream inputStreamForResourcePath(String path) {
313 		throw new UnsupportedOperationException("Method not yet implemented.");
314 		// TODO: Implement.
315 	}
316 
317 	public boolean isFramework() {
318 		return isFramework;
319 	}
320 
321 	public boolean load() {
322 		return loaded;
323 	}
324 
325 	public String name() {
326 		return name;
327 	}
328 
329 	/***
330 	 * @deprecated Don't use this method, use
331 	 *             resourcePathForLocalizedResourceNamed() instead.
332 	 */
333 
334 	public String pathForResource(String aName, String anExtension) {
335 		return this.resourcePathForLocalizedResourceNamed(aName, null);
336 	}
337 
338 	/***
339 	 * @deprecated Don't use this method, use
340 	 *             resourcePathForLocalizedResourceNamed() instead.
341 	 */
342 	public String pathForResource(String aName, String anExtension,
343 			String subDir) {
344 		return this.resourcePathForLocalizedResourceNamed(aName, subDir);
345 	}
346 
347 	/***
348 	 * @deprecated Don't use this method, use resourcePathsForResources()
349 	 *             instead.
350 	 */
351 	public NSArray pathsForResources(String aName, String anExtension) {
352 		throw new UnsupportedOperationException("Method not yet implemented.");
353 		// TODO: Implement.
354 	}
355 
356 	public Class principalClass() {
357 		return principalClass;
358 	}
359 
360 	public Properties properties() {
361 		return properties;
362 	}
363 
364 	/***
365 	 * @deprecated Resources are now accessed using the bytesForResourcePath()
366 	 *             and inputStreamForResourcePath() methods.
367 	 */
368 	public String resourcePath() {
369 		throw new UnsupportedOperationException("Method not yet implemented.");
370 		// TODO: Implement.
371 	}
372 
373 	public String resourcePathForLocalizedResourceNamed(String aName,
374 			String subDir) {
375 		throw new UnsupportedOperationException("Method not yet implemented.");
376 		// TODO: Implement.
377 	}
378 
379 	public NSArray resourcePathsForDirectories(String extension,
380 			String subdirPath) {
381 		throw new UnsupportedOperationException("Method not yet implemented.");
382 	}
383 
384 	public NSArray resourcePathsForLocalizedResources(String extension,
385 			String subdirPath) {
386 		throw new UnsupportedOperationException("Method not yet implemented.");
387 	}
388 
389 	public NSArray resourcePathsForResources(String extension, String subdirPath) {
390 		throw new UnsupportedOperationException("Method not yet implemented.");
391 
392 	}
393 
394 	public String toString() {
395 		int i = 0;
396 		if (classNames != null)
397 			i = classNames.count();
398 		return "<" + getClass().getName() + " name:'" + name + "' bundlePath:'"
399 				+ path + "' packages:'" + packages + "' " + i + " classes >";
400 	}
401 
402 	/* Static initialization code */
403 
404 	private static void _initLanguages() {
405 		_languageCodes.setObjectForKey("de", "German");
406 		_languageCodes.setObjectForKey("en", "English");
407 		_languageCodes.setObjectForKey("eo", "Esperanto");
408 		_languageCodes.setObjectForKey("es", "Spanish");
409 		_languageCodes.setObjectForKey("fr", "French");
410 		_languageCodes.setObjectForKey("ja", "Japanese");
411 	}
412 
413 	static {
414 		NSBundle._initLanguages();
415                                                                                
416 	}
417 
418 }