2

Alright so here is my problem and my question to you.....

I have a game in which it needs to load images from the jar file (all the images are packed into the jar file like so): I first have the unzip of the jar file: Unziped Jar File

Then it goes to: cards folder

Then inside each of those folder i have something that looks like this: what the other folders look like also

Now reminder above is the Jarfile GameClient.jar unziped and you see where the cards are in it.

So here is my code for trying to load each and every one of those images into memory

    private void addCardsAndChangeSize() throws IOException
{
    String tempString;
    ImageIcon tempIcon;
    allCards.clear();
    ClassLoader cldr = this.getClass().getClassLoader();
    URL imageURL;

    for(int i = 0; i < all.length; i++)
    {
        for(int x = 0; x < clubs.length; x++)
        {
            tempString = all[i][x];
            tempString = "/cards/"+cardFolders[i]+"/"+tempString;
            imageURL = cldr.getResource(tempString);
            tempIcon = resizeImage(new ImageIcon(imageURL),70,70,false);
            tempString = all[i][x];
            tempIcon.setDescription(tempString);
            allCards.add(tempIcon);
        }
    }
    backCard = resizeImage(new ImageIcon(cldr.getResource(back)),70,70,false);
}

So i call that in the contructor to load all the images into an ArrayList here is the whole class if you want to know what i do.

package global;

import java.awt.*;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;

import javax.imageio.ImageIO;
import javax.swing.*;

public class Cards
{
private ImageIcon backCard = new ImageIcon("cards/backCard.jpg");
private String back = "/cards/backCard.jpg";
private String[] cardFolders = {
        "clubs","diamonds","hearts","spades"
};
private String[] clubs = {
                              "aceClubs.jpg","eightClubs.jpg","fiveClubs.jpg","fourClubs.jpg","jackClubs.jpg",
        "kingClubs.jpg","nineClubs.jpg","queenClubs.jpg","sevenClubs.jpg","sixClubs.jpg",
        "tenClubs.jpg","threeClubs.jpg","twoClubs.jpg"
};
private String[] diamonds = {
        "aceDia.jpg","eightDia.jpg","fiveDia.jpg","fourDia.jpg","jackDia.jpg","kingDia.jpg",
        "nineDia.jpg","queenDia.jpg","sevenDia.jpg","sixDia.jpg","tenDia.jpg","threeDia.jpg",
        "twoDia.jpg"
};
private String[] hearts = {
        "aceHearts.jpg","eightHearts.jpg","fiveHearts.jpg","fourHearts.jpg","jackHearts.jpg",
        "kingHearts.jpg","nineHearts.jpg","queenHearts.jpg","sevenHearts.jpg","sixHearts.jpg",
        "tenHearts.jpg","threeHearts.jpg","twoHearts.jpg"
};
private String[] spades = {
        "aceSpades.jpg","eightSpades.jpg","fiveSpades.jpg","fourSpades.jpg","jackSpades.jpg",
        "kingSpades.jpg","nineSpades.jpg","queenSpades.jpg","sevenSpades.jpg","sixSpades.jpg",
        "tenSpades.jpg","threeSpades.jpg","twoSpades.jpg"
};
private String[][] all = {
        clubs,diamonds,hearts,spades
};
private ArrayList<ImageIcon> allCards = new ArrayList<ImageIcon>();

public Cards()
{
    try
    {
        addCardsAndChangeSize();
        shuffle();
    }
    catch(Exception e)
    {e.printStackTrace();}
}

/**
 * @param x Cards name with extension
 * @return Face Value of Card
 */
public static int getFaceValue(String x)
{
    int face = 0;
    switch(x)
    {
        case "aceClubs.jpg":
        case "aceDia.jpg":
        case "aceHearts.jpg":
        case "aceSpades.jpg":
            face = 1;
            break;
        case "eightClubs.jpg":
        case "eightDia.jpg":
        case "eightHearts.jpg":
        case "eightSpades.jpg":
            face = 8;
            break;
        case "fiveClubs.jpg":
        case "fiveDia.jpg":
        case "fiveHearts.jpg":
        case "fiveSpades.jpg":
            face = 5;
            break;
        case "fourClubs.jpg":
        case "fourDia.jpg":
        case "fourHearts.jpg":
        case "fourSpades.jpg":
            face = 4;
            break;
        case "jackClubs.jpg":
        case "jackDia.jpg":
        case "jackHearts.jpg":
        case "jackSpades.jpg":
        case "kingClubs.jpg":
        case "kingDia.jpg":
        case "kingHearts.jpg":
        case "kingSpades.jpg":
        case "queenClubs.jpg":
        case "queenDia.jpg":
        case "queenHearts.jpg":
        case "queenSpades.jpg":
        case "tenClubs.jpg":
        case "tenDia.jpg":
        case "tenHearts.jpg":
        case "tenSpades.jpg":
            face = 10;
            break;
        case "twoClubs.jpg":
        case "twoDia.jpg":
        case "twoHearts.jpg":
        case "twoSpades.jpg":
            face = 2;
            break;
        case "threeClubs.jpg":
        case "threeDia.jpg":
        case "threeHearts.jpg":
        case "threeSpades.jpg":
            face = 3;
            break;
        case "sixClubs.jpg":
        case "sixDia.jpg":
        case "sixHearts.jpg":
        case "sixSpades.jpg":
            face = 6;
            break;
        case "sevenClubs.jpg":
        case "sevenDia.jpg":
        case "sevenHearts.jpg":
        case "sevenSpades.jpg":
            face = 7;
            break;
        case "nineClubs.jpg":
        case "nineDia.jpg":
        case "nineHearts.jpg":
        case "nineSpades.jpg":
            face = 9;
            break;
    }
    return face;
}

//shuffles all the cards in the deck
private void shuffle()
{
    long seed = System.nanoTime();
    Collections.shuffle(allCards, new Random(seed));
}

/**
 * Chooses a card at random from the deck. Also removes that card when chosen
 * @return randomly chosen card
 */
public ImageIcon getRandomCard()
{
    int index = ((int)Math.random() * allCards.size());
    return allCards.remove(index);
}

/**
 * @return Image of the back of a card
 */
public ImageIcon getBackCard()
{return backCard;}

public static ImageIcon parseImage(String x)
{
    if(x.contains("Dia"))
        return new ImageIcon("cards/diamonds/"+x);
    else
        if(x.contains("Clubs"))
            return new ImageIcon("cards/clubs/"+x);
        else
            if(x.contains("Hearts"))
                return new ImageIcon("cards/hearts/"+x);
            else
                if(x.contains("Spades"))
                    return new ImageIcon("cards/spades/"+x);
    return null;
}

//adds all the cards and adds a description to them loaded into memory
private void addCardsAndChangeSize() throws IOException
{
    String tempString;
    ImageIcon tempIcon;
    allCards.clear();
    ClassLoader cldr = this.getClass().getClassLoader();
    URL imageURL;

    for(int i = 0; i < all.length; i++)
    {
        for(int x = 0; x < clubs.length; x++)
        {
            tempString = all[i][x];
            tempString = "/cards/"+cardFolders[i]+"/"+tempString;
            imageURL = cldr.getResource(tempString);
            tempIcon = resizeImage(new ImageIcon(imageURL),70,70,false);
            tempString = all[i][x];
            tempIcon.setDescription(tempString);
            allCards.add(tempIcon);
        }
    }
    backCard = resizeImage(new ImageIcon(cldr.getResource(back)),70,70,false);
}

//resizes images
public ImageIcon resizeImage(ImageIcon imageIcon, int width, int height, boolean max) 
{
    Image image = imageIcon.getImage();
    Image newimg = image.getScaledInstance(-1, height, java.awt.Image.SCALE_SMOOTH);
    int width1 = newimg.getWidth(null);
    if ((max && width1 > width) || (!max && width1 < width))
        newimg = image.getScaledInstance(width, -1, java.awt.Image.SCALE_SMOOTH);
    return new ImageIcon(newimg);
}

}

I keep getting null pointer exception on the tempIcon = resizeImage(new ImageIcon(imageURL),70,70,false);

Can anyone tell me why this isn't loading the images properly? Reminder i am pre-loading them into memory. My question is not how to make this better i am just simply asking what am i doing wrong with the class loader and stuff like that......

I am somewhat new at java and i know some of this code will look horrible to you but please again i am not asking how to make this code "pro" im just looking for the explanation why its giving me null.

Thanks!

P.S. If you need more pictures of info or w.e. i will try to put that up right away

3kings
  • 838
  • 2
  • 13
  • 28
  • 1
    Java is case sensitive when reading resources from Jar files. Are you sure that the file extension is '.jpg' and not '.JPG' ? – rolfl Nov 18 '13 at 23:51
  • It is .jpg not .JPG but thanks for that for further use – 3kings Nov 18 '13 at 23:57
  • Which card exactly is causing the problem? Is it the first card you try to load? There might be a typo somewhere in your big list of card filenames. You should check `imageURL` for null and if so throw an exception that gives you the filename that caused the problem. That might reveal the cause. – Geo Nov 19 '13 at 00:01
  • The first one i try to load. Also there are no typos going through it that was my first attempt at a solution. – 3kings Nov 19 '13 at 00:03
  • System.out.println(imageURL) – Black Maggie Nov 19 '13 at 00:36

3 Answers3

3

Your problem is that cldr.getResource(tempString); does not return object but null. Then, something is wrong with tempString. Classloader cannot find this file, so returns null.

Change tempString = "/cards/"+cardFolders[i]+"/"+tempString;

To tempString = "cards/"+cardFolders[i]+"/"+tempString;

Maciej Dobrowolski
  • 11,561
  • 5
  • 45
  • 67
  • I agree with the first half, but don't see how changing the path from an absolute to relative reference will help...unless the original path is wrong...just saying... – MadProgrammer Nov 19 '13 at 00:24
  • @MadProgrammer I have just written sample code and it seems to be difference for ClassLoader if path starts from "/" - possibly it looks for file in only one directory - unfortunately not the jar package root. – Maciej Dobrowolski Nov 19 '13 at 00:28
  • AFAIK `getClass().getResource()` will allow you to define the difference between a relative and absolute path, where as `ClassLoader#getResource` will always be absolute from the context of the classpath. You might be able to add using `getClass().getResource(...`)` instead of `getClass().getClassLoader().getResource(...)` instead – MadProgrammer Nov 19 '13 at 00:31
  • Thanks :) anyway `/` as a first character in classloader's `getResources()` does not well. Just check it out - it's 2 lines of code. – Maciej Dobrowolski Nov 19 '13 at 00:35
  • Already did ;) - Missing the difference `ClassLoader.getResource` and `getClass().getResource()` ;) – MadProgrammer Nov 19 '13 at 00:38
2

You should be using the getResource of Class, not ClassLoader. The getResource method in Class does useful transformations on the path before giving it to the ClassLoader version of the method, like deciding if the path is relative to the class or absolute, and so ClassLoader doesn't expect to see things like / at the start of the path. You can see this clearly in the getResource javadoc.

Geo
  • 750
  • 7
  • 17
  • Yes it can (*"see things like / at the start of the path"*), this makes the difference between and absolute path based on the class path and a relative path from the class loading it... – MadProgrammer Nov 19 '13 at 00:25
  • @MadProgrammer, Maybe some ClassLoaders can handle that, but for some reason Class.getResource removes the initial '/'. It's probably a good reason. – Geo Nov 19 '13 at 00:28
  • Okay, missed the point about `Classloader.getResource(...)` and `getClass().getResource(...);` – MadProgrammer Nov 19 '13 at 00:34
0

So i found out the solution i still don't quite understand why this is the answer but here ya go.

This is the revised code and works fine

private void addCardsAndChangeSize() throws Exception
{
    String tempString;
    ImageIcon tempIcon;
    allCards.clear();
    URL imageURL;

    for(int i = 0; i < all.length; i++)
    {
        for(int x = 0; x < clubs.length; x++)
        {
            tempString = all[i][x];
            tempString = "/cards/"+cardFolders[i]+"/"+tempString;
            imageURL = this.getClass().getResource(tempString);
            System.out.println(tempString);
            System.out.println(imageURL == null);
            tempIcon = resizeImage(new ImageIcon(imageURL),70,70,false);
            tempString = all[i][x];
            tempIcon.setDescription(tempString);
            allCards.add(tempIcon);
        }
    }
    backCard = resizeImage(new ImageIcon(this.getClass().getResource(back)),70,70,false);
}

You have to have that "/cards/" and also you this.getClass().getResource(String res);

Thanks for all your help guys!

3kings
  • 838
  • 2
  • 13
  • 28