Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||||||
NSBundle |
|
| 1.7878787878787878;1.788 |
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 | 0 | 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 | 0 | private static final NSMutableArray _allBundles = new NSMutableArray(); |
64 | ||
65 | 0 | private static final NSMutableArray _allFrameworks = new NSMutableArray(); |
66 | ||
67 | 0 | private static NSMutableDictionary _languageCodes = new NSMutableDictionary(); |
68 | ||
69 | 0 | private static NSBundle _mainBundle = null; |
70 | ||
71 | 0 | protected static NetworkClassLoader _classLoader = new NetworkClassLoader(ClassLoader.getSystemClassLoader()); |
72 | ||
73 | /* Instance variables */ |
|
74 | ||
75 | protected String name; |
|
76 | ||
77 | 0 | protected NSMutableDictionary info = null; |
78 | ||
79 | protected String path; |
|
80 | ||
81 | 0 | protected NSMutableArray classNames = new NSMutableArray(); |
82 | ||
83 | 0 | protected NSMutableArray packages = new NSMutableArray(); |
84 | ||
85 | protected Properties properties; |
|
86 | ||
87 | 0 | protected boolean isFramework = false; |
88 | ||
89 | 0 | 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 | 0 | public NSBundle() { |
101 | 0 | } |
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 | 0 | return _allBundles.immutableClone(); |
111 | } |
|
112 | ||
113 | /** |
|
114 | * @deprecated use frameworkBundles() to access any frameworks. |
|
115 | */ |
|
116 | public static NSArray allFrameworks() { |
|
117 | 0 | 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 | 0 | 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 | 0 | return bundleWithURL(new File(path).toURI().toURL()); |
144 | 0 | } catch (MalformedURLException e) { |
145 | 0 | NSLog.err.appendln("Bundle path is invalid: " + path); |
146 | 0 | 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 | 0 | NSBundle result = null; |
162 | 0 | String sep = System.getProperty("file.separator"); |
163 | 0 | String protocol = url.getProtocol(); |
164 | 0 | if (protocol.equals("file")) { |
165 | 0 | File f = new File(url.getPath()); |
166 | 0 | if (!f.exists()) { |
167 | 0 | NSLog.err.appendln("Bundle not found: " + url); |
168 | 0 | return null; |
169 | } |
|
170 | 0 | StringBuffer filename = new StringBuffer(f.getName()); |
171 | 0 | int extensionIndex = filename.lastIndexOf("."); |
172 | 0 | if (extensionIndex == -1) { |
173 | 0 | NSLog.err |
174 | 0 | .appendln("Named URL does not point to a bundle with an extension: " |
175 | 0 | + url); |
176 | 0 | return null; |
177 | } |
|
178 | 0 | String basename = filename.substring(0, extensionIndex); |
179 | 0 | String extension = filename.substring(extensionIndex + 1, filename |
180 | 0 | .length()); |
181 | 0 | System.out.println("basename: " + basename); |
182 | 0 | System.out.println("extension: " + extension); |
183 | 0 | result = new NSBundle(); |
184 | 0 | result.name = basename; |
185 | 0 | result.isFramework = extension.equals("framework"); |
186 | 0 | if (f.isDirectory()) { |
187 | try { |
|
188 | 0 | File javadir = new File(f.getCanonicalPath() + sep + "Contents" |
189 | 0 | + sep + "Resources" + sep + "Java"); |
190 | 0 | System.out.println(javadir); |
191 | 0 | System.out.println(javadir.exists()); |
192 | 0 | File[] jars = javadir.listFiles(); |
193 | ||
194 | 0 | } catch (IOException e) { } |
195 | 0 | } else { |
196 | 0 | throw new RuntimeException( |
197 | 0 | "Compressed bundle files not currently supported."); |
198 | } |
|
199 | 0 | throw new RuntimeException("Method not finished."); |
200 | } else { |
|
201 | try { |
|
202 | 0 | JarInputStream j = new JarInputStream(url.openStream()); |
203 | 0 | JarEntry entry1 = j.getNextJarEntry(); |
204 | ||
205 | JarFile f; |
|
206 | 0 | throw new RuntimeException("Method not finished."); |
207 | 0 | } catch (IOException e) { |
208 | 0 | NSLog.err |
209 | 0 | .appendln("IOException loading framework jar from URL " |
210 | 0 | + url + " - message: " |
211 | 0 | + e.getLocalizedMessage()); |
212 | 0 | StringWriter stacktrace = new StringWriter(); |
213 | 0 | e.printStackTrace(new PrintWriter(stacktrace)); |
214 | 0 | NSLog.err.appendln(stacktrace); |
215 | 0 | 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 | 0 | throw new UnsupportedOperationException("Method not yet implemented."); |
231 | // TODO: Implement. |
|
232 | } |
|
233 | ||
234 | public static synchronized NSArray frameworkBundles() { |
|
235 | 0 | 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 | 0 | _mainBundle = aBundle; |
248 | 0 | } |
249 | ||
250 | public static NSBundle mainBundle() { |
|
251 | 0 | 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 | 0 | String language = (String) _languageCodes.objectForKey(Locale |
262 | 0 | .getDefault().getLanguage()); |
263 | 0 | return language + ".lproj"; |
264 | } |
|
265 | ||
266 | protected static synchronized NSBundle findOrCreateBundleWithPath(String s) { |
|
267 | 0 | 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 | 0 | throw new UnsupportedOperationException("Method not yet implemented."); |
278 | // TODO: Implement. |
|
279 | } |
|
280 | ||
281 | public String bundlePath() { |
|
282 | 0 | 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 | 0 | throw new UnsupportedOperationException("Method not yet implemented."); |
294 | // TODO: Implement. |
|
295 | } |
|
296 | ||
297 | public NSArray bundleClassNames() { |
|
298 | 0 | return classNames.immutableClone(); |
299 | } |
|
300 | ||
301 | public NSDictionary infoDictionary() { |
|
302 | 0 | 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 | 0 | throw new UnsupportedOperationException("Method not yet implemented."); |
314 | // TODO: Implement. |
|
315 | } |
|
316 | ||
317 | public boolean isFramework() { |
|
318 | 0 | return isFramework; |
319 | } |
|
320 | ||
321 | public boolean load() { |
|
322 | 0 | return loaded; |
323 | } |
|
324 | ||
325 | public String name() { |
|
326 | 0 | 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 | 0 | 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 | 0 | 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 | 0 | throw new UnsupportedOperationException("Method not yet implemented."); |
353 | // TODO: Implement. |
|
354 | } |
|
355 | ||
356 | public Class principalClass() { |
|
357 | 0 | return principalClass; |
358 | } |
|
359 | ||
360 | public Properties properties() { |
|
361 | 0 | return properties; |
362 | } |
|
363 | ||
364 | /** |
|
365 | * @deprecated Resources are now accessed using the bytesForResourcePath() |
|
366 | * and inputStreamForResourcePath() methods. |
|
367 | */ |
|
368 | public String resourcePath() { |
|
369 | 0 | throw new UnsupportedOperationException("Method not yet implemented."); |
370 | // TODO: Implement. |
|
371 | } |
|
372 | ||
373 | public String resourcePathForLocalizedResourceNamed(String aName, |
|
374 | String subDir) { |
|
375 | 0 | throw new UnsupportedOperationException("Method not yet implemented."); |
376 | // TODO: Implement. |
|
377 | } |
|
378 | ||
379 | public NSArray resourcePathsForDirectories(String extension, |
|
380 | String subdirPath) { |
|
381 | 0 | throw new UnsupportedOperationException("Method not yet implemented."); |
382 | } |
|
383 | ||
384 | public NSArray resourcePathsForLocalizedResources(String extension, |
|
385 | String subdirPath) { |
|
386 | 0 | throw new UnsupportedOperationException("Method not yet implemented."); |
387 | } |
|
388 | ||
389 | public NSArray resourcePathsForResources(String extension, String subdirPath) { |
|
390 | 0 | throw new UnsupportedOperationException("Method not yet implemented."); |
391 | ||
392 | } |
|
393 | ||
394 | public String toString() { |
|
395 | 0 | int i = 0; |
396 | 0 | if (classNames != null) |
397 | 0 | i = classNames.count(); |
398 | 0 | return "<" + getClass().getName() + " name:'" + name + "' bundlePath:'" |
399 | 0 | + path + "' packages:'" + packages + "' " + i + " classes >"; |
400 | } |
|
401 | ||
402 | /* Static initialization code */ |
|
403 | ||
404 | private static void _initLanguages() { |
|
405 | 0 | _languageCodes.setObjectForKey("de", "German"); |
406 | 0 | _languageCodes.setObjectForKey("en", "English"); |
407 | 0 | _languageCodes.setObjectForKey("eo", "Esperanto"); |
408 | 0 | _languageCodes.setObjectForKey("es", "Spanish"); |
409 | 0 | _languageCodes.setObjectForKey("fr", "French"); |
410 | 0 | _languageCodes.setObjectForKey("ja", "Japanese"); |
411 | 0 | } |
412 | ||
413 | static { |
|
414 | 0 | NSBundle._initLanguages(); |
415 | ||
416 | 0 | } |
417 | ||
418 | } |