<HTML><BODY style="word-wrap: break-word; -khtml-nbsp-mode: space; -khtml-line-break: after-white-space; "><DIV>This is a tutorial on how to redefine resources to your advantage.</DIV><DIV><BR class="khtml-block-placeholder"></DIV><DIV>It was motivated by Manfred's question on replacing icons. I will be using that as a final example.</DIV><DIV><BR class="khtml-block-placeholder"></DIV>We have a developer friendly resource lookup system in JSword. At the heart of it is a custom class loader called org.crosswire.common.util.CWClassLoader which uses a helper class org.crosswire.common.CallContext. CallContext returns the class of the caller.<DIV><BR class="khtml-block-placeholder"></DIV><DIV>The significant difference of CWClassLoader is it search algorithm.</DIV><DIV><BR class="khtml-block-placeholder"></DIV><DIV>The static method CWClassLoader.setHome establishes a program's notion of its home. We call this with ~/.jsword, ~/Application Data/JSword, or ~/Library/Application Support/JSword for unix, windows and mac respectively.</DIV><DIV><BR class="khtml-block-placeholder"></DIV><DIV>We don't use CWClassLoader directly but use org.crosswire.common.util.ResourceUtil.getResource([Class clazz], String resourceName).</DIV><DIV><BR class="khtml-block-placeholder"></DIV><DIV>But the meat of the lookup is in CWClassLoader's method findResource(resourceName), which returns an URL to the resource or null, if not found. The trick in the whole of it is that the standard ClassLoader will look for resources with a relative path in the calling class's package and with a relative path will do a full search along the CLASSPATH. We essential cheat by algorithmically pre-appending "getHome()" to it. We also look for resources either as a file in the root of the path with package dot notation or along a directory path to the package.</DIV><DIV><BR class="khtml-block-placeholder"></DIV><DIV>Here is the order of the lookup for a resource foo in package org.crosswire.common.utils.Bar:</DIV><DIV>(Let ~ be the equivalent of getHome() and not the user's home)</DIV><DIV>~/org.crosswire.common.utils.Bar.foo.properties</DIV><DIV>~/org/crosswire/common/utils/Bar/foo.properties</DIV><DIV>common.jar#org.crosswire.common.utils.Bar.foo.properties</DIV><DIV>common.jar#org/crosswire/common/utils/Bar/foo.properties</DIV><DIV><BR class="khtml-block-placeholder"></DIV><DIV>The last is the actual resource as we currently store it in the jar.</DIV><DIV>The second to last is how we used to store them in the jar.</DIV><DIV>The first is the easiest way to override resources.</DIV><DIV>The second is used when running code directly from java files or a file system with class files.</DIV><DIV><BR class="khtml-block-placeholder"></DIV><DIV>The second context where we use CWClassLoader is in handling internationalization (aka i18n).</DIV><DIV>When we want to lookup an internationalize resource we use CWClassLoader in a call to ResourceBundle.getBundle(...) as in:</DIV><DIV style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; "><FONT class="Apple-style-span" face="Monaco" size="3"><SPAN class="Apple-style-span" style="font-size: 11px;"><BR class="khtml-block-placeholder"></SPAN></FONT></DIV><DIV style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; "><FONT class="Apple-style-span" face="Monaco" size="3"><SPAN class="Apple-style-span" style="font-size: 11px;">ResourceBundle resources = ResourceBundle.getBundle(basis.getName(), Locale.getDefault(), </SPAN></FONT><FONT class="Apple-style-span" color="#7F0055" face="Monaco" size="3"><SPAN class="Apple-style-span" style="font-size: 11px;">new</SPAN></FONT><FONT class="Apple-style-span" face="Monaco" size="3"><SPAN class="Apple-style-span" style="font-size: 11px;"> CWClassLoader(basis));</SPAN></FONT></DIV><DIV><BR class="khtml-block-placeholder"></DIV><DIV>where basis is the Class that owns the resource.</DIV><DIV><BR class="khtml-block-placeholder"></DIV><DIV>The net upshot is that for a German in Germany the lookup is for the following names:</DIV><DIV>foo_de_DE.properties</DIV><DIV>foo_de.properties</DIV><DIV>foo.properties</DIV><DIV>Now the lookup is per entry in the properties. This means that to override what is in foo.properties one only needs to create a more specific resource for their own locale.</DIV><DIV><BR class="khtml-block-placeholder"></DIV><DIV>The point of this is that any of the files that are loaded with ResourceBundle (and just about all are) you can override it incrementally by creating a locale specific semi-copy of the file.</DIV><DIV><BR class="khtml-block-placeholder"></DIV><DIV>For me, my locale is en_US. Since there is no en or en_US defined already, I can create foo_en.properties as a copy of foo.properties and then delete all the keys I don't want to change.</DIV><DIV><BR class="khtml-block-placeholder"></DIV><DIV>For a real example, let's say I want to override the desktop icons. These are defined in Desktop.properties in the form Action.smallIcon and Action.largeIcon. I would create a copy called org.crosswire.bibledesktop.desktop.Desktop_en.properties and place that in ~/Library/Application Support/JSword (e.g. on a Mac, see above for other locations) Then I'd delete all the lines that did not define smallIcon or largeIcon as well as the ones without values. Finally, change the values to the names of the icons you wish to use. Put these icons in ~/Library/Application Support/JSword. You might want to create an images directory there. Then the value would be something like images/cutSmall.png</DIV><DIV><BR class="khtml-block-placeholder"></DIV><DIV><BR class="khtml-block-placeholder"></DIV><DIV>This is not how we should do image replacement, but it serves as a quick way to test an idea to see if it is worthwhile to pursue.</DIV><DIV><BR class="khtml-block-placeholder"></DIV><DIV>It is also an easy way to share an idea, just create a zip of the new properties file and the images directory and give that out.</DIV><DIV><BR class="khtml-block-placeholder"></DIV></BODY></HTML>