My aim is to write an android Library that can list all the resources (layouts, drawables. ids, etc.) of the caller application. The caller application need to pass to the library, nothing more than App Name or the package namespace.
E.g:
The MyLibrary function:
public void listDrawables(String namespace) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
String resourceNameSpace = namespace + ".R";
String drawableNamespace = resourceNameSpace + ".drawable";
final Class drawableClass = Class.forName(drawableNamespace);
Object drawableInstance = drawableClass.newInstance();
final Field[] fields = drawableClass.getDeclaredFields();
for (int i = 0, max = fields.length; i < max; i++) {
final int resourceId;
try {
resourceId = fields[i].getInt(drawableInstance);
// Use resourceId further
} catch (Exception e) {
continue;
}
}
The caller code (From the App Activity, say com.example.sample.MainActivity.java)
mylibrary.listDrawables("com.example.sample");
I have setup the MyLibrary as a dependent android library for the Sample app.
When this is run, I get this exception inside library at the Class.forName statement:
java.lang.ClassNotFoundException: com.example.sample.R.drawable
I am not able to understand fully, why library can't refer to the class. May be I missed a basic lesson in Java classpath and how build works, but isn't the class already loaded when library runs?
I would also like to know id there is any alternative way to list resources in an external library.
Since Drawable is a inner class, you need to access it by using $.
String resourceNameSpace = namespace + ".R";
String drawableNamespace = resourceNameSpace + "$drawable";
Related
I am really stumped. I'm just an old C X11/Motif programmer trying to write a little Java program. After a week of reading the Oracle Java Documentation, as well as the
Stack Overflow answers related to getResource, I still can not figure out how to retrieve the path to the icon files in my jar file.
My icons are contained within the jar file for my application. I wish to access them using the relative position within jar file. I am assuming the best way to do this is through the getResource method.
The core part of my code for my program called Fŭd (pronounced food - like the cat spells it in the comic strip "Get Fuzzy") is as follows:
package localhost.system1;
imports not shown for brevity.
public class Fud extends JPanel
implements FocusListener, ActionListener, ItemListener
{
private static final long serialVersionUID = 1L;
static Food data = null;
static int prev = 0;
static int next = 1;
static int plus = 2;
static int minus = 3;
public static void main(String args[]) throws Exception
{
LocalDate now = LocalDate.now();
int dateDifference = 0;
// load in the existing data
data = new Food(programName);
data.loadFood(programName);
// test to see if data is up to date. Add days if not
dateDifference = Math.abs((int)ChronoUnit.DAYS.between(now, data.day[0].date));
if ( dateDifference != 0)
{
data.adjustToToday(dateDifference, programName);
}
/////////////////////////////////////////////////
// create the GUI and switch running over to it.
/////////////////////////////////////////////////
Fud fud = new Fud();
Class<? extends Fud> fudClass = fud.getClass();
String className = fudClass.getName();
System.out.println("fudClass getname returns " + className);
URL testURL = fudClass.getResource("prev.png");
System.out.println("fudClass getResource returned " + testURL);
// Create GUI and turn the control over to it
javax.swing.SwingUtilities.invokeLater
(new Runnable()
{
public void run()
{
URL[] iconURL = new URL[4];
iconURL[prev] = Fud.class.getResource("prev.png");
iconURL[next] = Fud.class.getResource("next.png");
iconURL[plus] = Fud.class.getResource("plus.png");
iconURL[minus] = Fud.class.getResource("minus.png");
createAndShowGUI(fud, iconURL);
}
}
);
} // end of main
.
.
.
Rest of methods and subroutines needed
.
.
.
}
When run, the code returns the following results:
fudClass getname returns localhost.system1.Fud
fudClass getResource returned null
This has me quite frustrated. No matter what I try (and I have tried a number of things) the result remains the same. I keep getting NULL for a response from the getResource method. When I query the jar file with jar -tf Fud.jar I get the following:
jar tf Fud.jar
META-INF/MANIFEST.MF
localhost/
localhost/system1/
localhost/system1/Day.class
localhost/system1/Food.class
localhost/system1/Fud$1.class
localhost/system1/Fud$2.class
localhost/system1/Fud$3.class
localhost/system1/Fud$4.class
localhost/system1/Fud$5.class
localhost/system1/Fud$6.class
localhost/system1/Fud$7.class
localhost/system1/Fud.class
minus.png
next.png
plus.png
prev.png
So the icons are in the Jar file. Can anyone tell me what I am doing wrong? In Eclipse, my project explorer looks like:eclipse Project Explorer
I added the Image directory to my project Java build in eclipse as follows: Eclipse Java Build
I built the program using Eclipse Version: 2021-12 (4.22.0) Build id: 20211202-1639. Furthermore, I am using Java 17.0.1 2021-10-19 LTS on Windows 11 Pro build 22000.434.
You have to add a slash in front of the resource:
Fud.class.getResource("/prev.png");
otherwise java searching in the same folder as the class is located,
so it will search in localhost/system1
During my searching, I would like a piece of advice about the following situation :
the guy, on my website, choose a parcel to send, when he validates the choice, some carriers appear as results. Now some carriers have different offers with different logos located in a special directory.
Now the business logic I would like to use is :
If in the directory I find the peculiar logo corresponding to the special offer, I will take the logo to display It in the web page with the special offer.
I choose to do this work with the ResourceTool from Velocity
I have to implement 2 methods getLogo() and getLabel().
The getLogo() will look for the special logo.
I think to use this method to recover the object :
public static ResourceNode getResource(Context context, ResourceType resourceType, String...keys) {
try {
if (null != ResourcesTool.instance) {
ResourceNode resource = ResourcesTool.instance.getResourceSet(context, resourceType);
if (null != resource) {
Deque < String > keyDeque = new ArrayDeque < > ();
for (String key: keys) {
keyDeque.add(key);
}
return (ResourceNode) resource.sub(keyDeque);
}
}
} catch (Exception e) {
BoxtaleLogger.debug("[ResourcesTool.getResource] error: ", e);
}
return null;
}
Now I am searching a example to merely use this method to recover the different .jpg
question 2 : I don't understand what is the meaningful of Context context in this method ?
Then the resourceType is an enum either a String or a picture (the logo in fact)
All right I found It :
public String getLogo(ResourceNode node){
//readable variables
String ope_code = (String)((Instance)get("operateur")).get("ope_code");
String paysDest = ((Instance)db.getEntity("tab_pays").fetch((Integer) get("pz_id"))).get("pz_iso");
String path = node.get(ope_code+"_"+get("srv_code")+"_"+paysDest+".png");
if (path=null){
path = node.get(ope_code+"_"+get("srv_code")+".png");
if (path=null){
path = node.get(ope_code+".png");
}
}
return path ;
}
Now I am testing the method, I will tell you after.
I have a file in the res/raw folder called "book1tabs.txt", but in general I will not know what it is called. Then I have to do something like follows:
InputStream in = this.mCtx.getResources().openRawResource(R.raw.book1tabs);
But I want to use a string variable, like
String param = "book1tabs";
And be able to open up that same input stream.
Is there any way to do this?
Thanks
You can do something like this
String param = "book1tabs";
InputStream in = this.mCtx.getResources().openRawResource(mCtx.getResources().getIdentifier(param,"raw", mCtx.getPackageName()));
getIdentifier() will return the id from R.java of particular parameter you passed.
Exactly what it will do is as follow
Maps the Package Name
Navigate to typeDef you provided
Finds the resource name you provided in the typeDef
For more information http://developer.android.com/reference/android/content/res/Resources.html
I find this method to be very useful to pull all sorts of resources by their string names...
#SuppressWarnings("rawtypes")
public static int getResourceId(String name, Class resType){
try {
Class res = null;
if(resType == R.drawable.class)
res = R.drawable.class;
if(resType == R.id.class)
res = R.id.class;
if(resType == R.string.class)
res = R.string.class;
if(resType == R.raw.class)
res = R.raw.class;
Field field = res.getField(name);
int retId = field.getInt(null);
return retId;
}
catch (Exception e) {
// Log.d(TAG, "Failure to get drawable id.", e);
}
return 0;
}
This will return the numeric id (assuming such a resource exists). For the Class pass in R.drawable and for the string whatever it's xml based ID name is.
I always have this method hanging around all my projects for easy access.
I'm trying to get require.js to load modules on the server-side with Java 6 and Rhino.
I'm able to load require.js itself just fine. Rhino can see the require() function. I can tell because Rhino complains that it can't find the function when I change require() to something else like requireffdkj().
But when I try to require even a simple JS, like hello.js
var hello = 'hello';
using either of the following:
require('hello');
require('./hello');
it doesn't work. I get
Caused by: javax.script.ScriptException: sun.org.mozilla.javascript.internal.JavaScriptException: [object Error] (<Unknown source>#31) in <Unknown source> at line number 31
at com.sun.script.javascript.RhinoScriptEngine.eval(RhinoScriptEngine.java:153)
at com.sun.script.javascript.RhinoScriptEngine.eval(RhinoScriptEngine.java:167)
at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:247)
I have my hello.js at the top of the Java classpath. That's where I have require.js as well. I tried moving hello.js everywhere I could think it might possibly go, including the root of my hard drive, the root of my user directory, the directory from which I'm running my Java app, etc. Nothing works.
I looked at the CommonJS spec (http://wiki.commonjs.org/wiki/Modules/1.0) and it says that top-level IDs (like hello) are resolved from the "conceptual module name space root", whereas relative IDs (like ./hello) are resolved against the calling module. I'm not sure where either of those baselines is, and I suspect that's the issue.
Any suggestions? Can I even use require.js from Rhino?
EDIT: Thinking that I need to set the environment up as per Pointy's suggestion in the comment below, I tried evaluating r.js as well. (I tried evaluating after evaluating require.js, and then again before require.js.) In either case I get an error:
Caused by: javax.script.ScriptException: sun.org.mozilla.javascript.internal.EcmaError: ReferenceError: "arguments" is not defined. (<Unknown source>#19) in <Unknown source> at line number 19
at com.sun.script.javascript.RhinoScriptEngine.eval(RhinoScriptEngine.java:153)
at com.sun.script.javascript.RhinoScriptEngine.eval(RhinoScriptEngine.java:167)
at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:247)
"arguments" appears to be a variable in r.js. I think it's for command line arguments, so I don't think r.js is the right path for what I'm trying to do. Not sure though.
require.js works well with rhino. Recently, I used it in a project.
You have to make sure to use r.js (not require.js) , modified version of require.js for rhino.
You have to extend ScritableObject class to implement load and print function. When you call require(["a"]), the load function in this class will be called, you can tweak this function to load the js file from any location. In the below example, I load from classpath.
You have to define the property arguments in the sharedscope as shown below in the sample code
Optionally, you can configure the sub path using require.config, to specify the subdirectory inside classpath where js files are located.
JsRuntimeSupport
public class JsRuntimeSupport extends ScriptableObject {
private static final long serialVersionUID = 1L;
private static Logger logger = Logger.getLogger(JsRuntimeSupport.class);
private static final boolean silent = false;
#Override
public String getClassName() {
return "test";
}
public static void print(Context cx, Scriptable thisObj, Object[] args,
Function funObj) {
if (silent)
return;
for (int i = 0; i < args.length; i++)
logger.info(Context.toString(args[i]));
}
public static void load(Context cx, Scriptable thisObj, Object[] args,
Function funObj) throws FileNotFoundException, IOException {
JsRuntimeSupport shell = (JsRuntimeSupport) getTopLevelScope(thisObj);
for (int i = 0; i < args.length; i++) {
logger.info("Loading file " + Context.toString(args[i]));
shell.processSource(cx, Context.toString(args[i]));
}
}
private void processSource(Context cx, String filename)
throws FileNotFoundException, IOException {
cx.evaluateReader(this, new InputStreamReader(getInputStream(filename)), filename, 1, null);
}
private InputStream getInputStream(String file) throws IOException {
return new ClassPathResource(file).getInputStream();
}
}
Sample Code
public class RJsDemo {
#Test
public void simpleRhinoTest() throws FileNotFoundException, IOException {
Context cx = Context.enter();
final JsRuntimeSupport browserSupport = new JsRuntimeSupport();
final ScriptableObject sharedScope = cx.initStandardObjects(browserSupport, true);
String[] names = { "print", "load" };
sharedScope.defineFunctionProperties(names, sharedScope.getClass(), ScriptableObject.DONTENUM);
Scriptable argsObj = cx.newArray(sharedScope, new Object[] {});
sharedScope.defineProperty("arguments", argsObj, ScriptableObject.DONTENUM);
cx.evaluateReader(sharedScope, new FileReader("./r.js"), "require", 1, null);
cx.evaluateReader(sharedScope, new FileReader("./loader.js"), "loader", 1, null);
Context.exit();
}
}
loader.js
require.config({
baseUrl: "js/app"
});
require (["a", "b"], function(a, b) {
print('modules loaded');
});
js/app directory should be in your classpath.
Yes, the Internet says - "unzip them all, decompile and compare the code with some tool (total comander, WinCompare, meld(linux), whatever...) The reason why I need a tool to generate difference report automatically from Fodler1 and Folder2 is simple - there are too much JARs in these Folders and I need to compare these folders (with next version of Jars) say 1 time in a month. So, I really do not want to do it manually at all!
Let's see what I've got so far:
1) I can find all JARs in each Folder:)
2) I can get the list of classes from each JAR:
private static void AddAllClassesFromJAR(JarInputStream jarFile,
ArrayList<String> classes) throws IOException {
JarEntry jarEntry = null;
while (true) {
jarEntry = jarFile.getNextJarEntry();
if (jarEntry == null) {
break;
}
if (jarEntry.getName().endsWith(".class")) {
classes.add(jarEntry.getName());
}
}
}
public static List<String> getClasseNamesInPackage(String jarName) {
ArrayList<String> classes = new ArrayList<String>();
try {
JarInputStream jarFile = new JarInputStream(new FileInputStream(jarName));
AddAllClassesFromJAR(jarFile, classes);
} catch (Exception e) {
e.printStackTrace();
}
return classes;
}
3) There is Reflection in Java (Core Java 2 Volume I - Fundamentals, Example 5-5), so I can get the list of methods from one class once I know its name.
In order to do that I need to make an instance of each class, the problem is how can I make the instance of each Class which I got from each JAR file?
Now I'm loading each JAR:
loader_left = new JarClassLoader("left/1.jar");
public class JarClassLoader extends URLClassLoader {
public JarClassLoader( URL url ) {
super( new URL[]{url} );
}
public JarClassLoader( String urlString ) throws MalformedURLException {
this( new URL( "jar:file://" + urlString + "!/" ) );
}
No exceptions, but I can not find any resource in it, trying to load the class like:
class_left = loader_left.loadClass("level1.level2.class1");
And getting "java.lang.ClassNotFoundException".
Any glue where is the problem? (class name is verified. it is hardcoded just for testing, ideally it should get it from the list of the classes)
Second question: since most of the classes in Folder1 and Folder2 will be same, what will happen if I load the same class second time (from Fodler2)?
Try the jarcomp utility.
This is not directly answering your question, however you may get a look to the ASM framework . This allows you to analyze bytecode without having to load the classes with the class loader. It is probably easier to do it this way.