views:

59

answers:

2

Hey everyone,

I was adding a loading bar feature to my program in Java and I was wondering if I could set a list of processes that need to be done, so that it knows how many processes it needs to complete.

For example, saving them as strings?

ArrayList<String> processes = new ArrayList<String>();
processes.add("CSVWriter.createFileOfCompany(\"Random Company\");");
processes.add("FileConverter.convertCSVToXLS(classPath + 
    \"/Output/Random_Company.csv\");");

for (int i = 0; i < processes.size(); i++) {
    // run process
    // update loading bar
}

These aren't real methods to my program, but they are pretty similar to what I want to accomplish.

I'm sure this isn't the best way, but I want to have some way to create a list like this so I know how many processes there are before I run them. I have a system set up to break down these processes even further and show their progress, so this bar is pretty precise at the moment, but I have to number each of the processes =/.

Maybe I'm just missing the point. Creating progress bars is totally new to me.

If there are any good articles on progress bar creation, feel free to send them my way as well. Keep in mind that I'm not using an actual swing-based GUI. This bar is all S.O.P text.

Many thanks,

Justian Meyer

+5  A: 

Closures will hopefully be coming soon in the next version of Java, but until then you can use anonymous classes implementing a known interface:

List<Runnable> jobs = new ArrayList<Runnable>();

jobs.add(new Runnable() {
    public void run() {
        CSVWriter.createFileOfCompany("Random Company");
    }
});

jobs.add(new Runnable() {
    public void run() {
        FileConverter.convertCSVToXLS(classPath + "/Output/Random_Company.csv");
    }
});

for (Runnable job : jobs) {
    job.run();
}
Gunslinger47
Why not just use Runnable or Callable (if you need to be able to throw exception) instead of creating own 'Job' interface.
Peter Štibraný
Peter, I beat you to it :-)
sanity
Thanks for the edit, @Sanity. I was in the middle of it myself. I decided to go with a custom interface originally to better illustrate the idea, however that was just adding noise.
Gunslinger47
Yes, maybe for explaining the idea, custom interface was better :-)
Peter Štibraný
Oh! I remember seeing this concept floating around in one of my old textbooks. Such a cool idea :). Thanks so much for the help. +1 for clean post and +1 for sanity for great edit.
Justian Meyer
+1  A: 

Here's a scheme that just came to my mind:

interface WorkProcess
    void setWorkUnits(int units)
    void setArguments(Object obj1,...)
    void execute()

So you encapsulate all your tasks with an interface that does execute as per the classic command pattern; it's also told (see below) how much work that job will probably take. You'll probably want some mechanism to pass data into these tasks for them to work with.

class WorkProcessFactory
    static WorkProcess makeWorkProcess()
    static int getTotalWorkUnitsAllocated()
    static synchronized int reportWorkDone(int units)
    static void addProgressListener(ProgressListener xxx)

When you have a job to do, you ask the factory to churn out one of these processes for you. In doing so, it does the estimate on work units and passes that to the WorkProcess it just created. It also keeps a tally of the total of those units for setting up the top of the progress indicator. One cool side effect of this is that you don't have to wait for all your jobs to be prepared: You can increment the maximum value for your progress report every time you add a job even as jobs are being processed and newly created; your bar will always show a realistic ratio.

You can stuff your WorkProcesses into a queue of some sort. ExecutorService comes to mind if your WorkProcess also implements the Runnable interface (which is a good idea). You'll be at liberty to process those jobs sequentially single-file or concurrently - whatever your execution queue machinery supports.

The WorkProcess' execute() method basically wraps the run() method (or maybe the other way around, since ExecutorService expects a run() method but our work unit magic is in execute()); but when that's done, it conscientiously calls back to reportWorkDone with the number of units it was told its job was worth. Upon receiving these reports, the factory has an updated value for units of work done, which it can report back to the snooping ProgressListener. That, in turn, updates the bar in the GUI.

Done. (I think). Probably needs some refining.

Carl Smotricz