This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

Bug 58633 - Provide API for creating custom output components
Summary: Provide API for creating custom output components
Status: RESOLVED FIXED
Alias: None
Product: platform
Classification: Unclassified
Component: Output Window (show other bugs)
Version: 5.x
Hardware: All All
: P2 blocker (vote)
Assignee: apireviews
URL:
Keywords: API
: 96402 (view as bug list)
Depends on:
Blocks:
 
Reported: 2005-05-05 16:34 UTC by Marian Petras
Modified: 2009-02-23 14:09 UTC (History)
5 users (show)

See Also:
Issue Type: ENHANCEMENT
Exception Reporter:


Attachments
Diff to provide the necessary API (19.77 KB, text/plain)
2006-08-14 07:58 UTC, _ tboudreau
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Marian Petras 2005-05-05 16:34:56 UTC
I would like to create a custom TopComponent for displaying output from an Ant
task. The output from a single Ant session would be parsed and split into three
tabs of the TopComponent. I need to keep the ability of creating hyperlinks.

The concrete goal I wish to achieve is described in the UI specification for the
JUnit, in the section about the JUnit output window - see
http://ui.netbeans.org/docs/ui/junits/promo_f.html#CHAPTER4.
Comment 1 Milos Kleint 2005-05-05 17:11:11 UTC
to Rudolf:
I believe the UI design is flawed.
1. splitting the output was done in vcs before and it never actually worked
well. If you get multiple outputs from multiple projects, you get tabs in tabs..
2. *a lot* of clicking on tabs, trying to search for what is going on and what
was printed where.
3. how do I easily figure the context of the error/failure in the "test output"? 

to Marian:
what kind of API would you envision?
Comment 2 _ tboudreau 2006-02-04 12:21:21 UTC
Simplest way to handle it in the output window would probably be to have a 
method that will return something like

public interface OutputWindowProvider {
   public IOProvider getIOProvider();
   public JComponent getOutputComponent();
}

Then you'd need a bit of event handling magic in IOEvent.dispatch() to not tie 
it to OutputWindow.DEFAULT, which ought to be trivial - there's already a 
comment there about how to do it.

You might want to make AbstractOutputWindow not subclass TopComponent, but be 
a panel within a TopComponent, just to de-complexify things a little bit and 
not be throwing around TopComponents that aren't actually managed by the 
window system.

Anyway, it ought to be a relatively small set of patches to make this work.  
The owner of the component then just calls getIOProvider() and uses it in the 
usual output way - they're just not using IOProvider.getDefault().
Comment 3 _ tboudreau 2006-08-14 07:58:20 UTC
Created attachment 32869 [details]
Diff to provide the necessary API
Comment 4 _ tboudreau 2006-08-14 08:05:40 UTC
Since it seems this issue will be very important if we want to have a useful
JUnit window, I've attached a patch (with tests) which does a few minor
modifications to the output window to remove the assumption that there is only
one output window (it helps that it was always designed to make this possible
with a minimum of effort - see comments about reentrancy in IOEvent.dispatch()).

It introduces two new abstract classes.  These should be put in an API package -
in the patch they are in org.netbeans.core.output2 because I didn't want to
create a new permanent folder in CVS just to provide a proper diff.

public abstract class IOComponentFactory {
    protected IOComponentFactory();
    /**
     * Get an object which can provide its own output component
     * and InputOutput instances that talk to it.
     */ 
    public static IOComponentFactory getDefault();
    /**
     * Create a new IOComponentManager - call this method when a new output 
     * component is needed with its associated IOProvider for getting input
     * and output streams that read from/write to the output component.
     */ 
    public abstract IOComponentManager newComponentManager();
}

public abstract class IOComponentManager {
    /**
     * Get the output component where this object's IOProvider will create
     * output tabs and write output.  This method will always return the same
     * output window instance as long as the previously returned output window
     * instance has not been garbage collected.  Whether or not it will be
     * strongly referenced from within the output window module is up to the
     * implementation.
     * 
     * @return a component in which output text components will be created on
     *  demand when InputOutput instances are requested from the associated
     *  IOProvider.
     */
    public abstract Component getOutputComponent();
    /**
     * Get the IOProvider associated with this output component - the factory
     * for InputOutput objects whose streams can read from and write to 
     * output window components inside the container returned by 
     * <code>getOutputComponent()</code>.
     */ 
    public abstract IOProvider getIOProvider();
    
    /**
     * Convenience method which delegates to <code>getIOProvider().getIO()</code>.
     */ 
    public final InputOutput getInputOutput (String name, boolean reuse) {
        return getIOProvider().getIO (name, reuse);
    }
    
    /**
     * Convenience method which delegates to    
     *  <code>getIOProvider().getIO()</code>.
     */ 
    public final InputOutput getIO (String name, Action[] toolbarActions) {
        return getIOProvider().getIO (name, toolbarActions);
    }
}

Is org.netbeans.api.output an appropriate package for the API classes?
Comment 5 Milos Kleint 2006-08-15 12:23:37 UTC
the api looks good, i'm not so fond of the test. Any test that sleeps for 20
seconds is bad by definition.

Feel free to submit it for api review if you need it ASAP, I'm not sure I will
be able to do it myself in the coming weeks.
Comment 6 Jesse Glick 2006-08-16 18:07:23 UTC
-1 on creating a new API distinct from org.openide.io.


Get rid of the convenience methods. (In particular, they are not named
consistently: getInputOutput vs. getIO.) There are only expected to be one or
two callers anyway.
Comment 7 ivan 2006-08-16 19:31:59 UTC
I don't get it, how do you connect the Component and the IO that
you get separately from IOComponentManager?

Also, I'm a bit concerned over the entrenchment of the tabs ... which can't be
pulled out into a separate mode and are not controlled by groups.
For example when using dbx under the gui onelikes ot have the dbx
cmdline as well as the output of the debuggee
simultaneously available. Right now we have our own self-managed 
top-components which look incongruent with the tabs in Output. In a sense
we already have the tab-within tab experience: The output mode contains some
top-comonents which you switch between using topcomponent "tabs". One of them
is the Output window with it's own tabs.
Comment 8 _ tboudreau 2006-08-17 07:35:36 UTC
> -1 on creating a new API distinct from org.openide.io.

It can easily be in org.openide.io...actually, there is there no org.openide.io
package (well, there's the io module's bundle.properties there).

What I'd *really* like to do is deprecate all of the IO API, write replacement
that fixes some of the weirdness (methods on OutputWriter that belong on
IOProvider, etc.);  there's not that much there so it would be easy to have
core/output2 just work with the old or new version.

Re the 20ms sleep, that's just me being lazy - easy to do another way;  it's
just making sure some things get processed on the EQ before we check them.

Convenience methods - easy enough to do.
Comment 9 Milos Kleint 2006-08-30 07:35:54 UTC
there's some other related output window api enhancements like #60862 and #82647
which all try to get around the fact that current IO api is not easily extendible. 

I suggest we pull all of them from review and create a new api that would handle
the new usecases in a consistent manner.
Comment 10 _ tboudreau 2006-08-30 16:22:54 UTC
I've been thinking the same thing and nearly suggested it a few times.  It
wouldn't be hard to support both.

My only worry with that approach is that some of these improvements are needed
soon - doing individual items will get the features people need in;  we need to
be *sure* that we'll actually be able to deliver a result.  There are a
certainly some things in the existing API that could stand to be cleaned up.

Hmmm...perhaps we can try using our nifty new wiki on netbeans.info for some
design discussions.
Comment 11 Maros Sandor 2007-03-02 13:54:21 UTC
Will "custom components (tabs) in Output Window" make it into 6.0?
Comment 12 Milos Kleint 2007-03-02 14:20:18 UTC
*** Issue 96402 has been marked as a duplicate of this issue. ***
Comment 13 Maros Sandor 2007-03-05 13:39:24 UTC
This partially blocks Issue #65584. Not a real blocker but I would not have to
re-invent and re-write Output window. Is anybody still working on this? If so,
it would be nice to have it for 6.0. Issue #65584 has 15 votes so I will have to
fix it one way or the other.
Comment 14 Milos Kleint 2007-03-05 14:09:15 UTC
nooone working on this AFAIK. At lest I don't have it in my plan.
Comment 15 _ tboudreau 2007-03-07 08:01:06 UTC
Well, all of this kind of stalled when we decided what we really should do is
replace the I/O API en banc.  There's no particular reason to tie the two things
together.  I could integrate the patch as-is.  What do you think?
Comment 16 Milos Kleint 2007-03-07 08:25:11 UTC
-1 from me.
Comment 17 _ tboudreau 2007-03-28 04:00:46 UTC
Well, this RFE could have a big positive impact on the usability of the JUnit
module.  It would be good if we could get this functionality available before
feature freeze.

Milos, I am attempting to work with your sketched API and actually implement it.
 There are a few things I don't quite understand:  There is LineContext, which
we discussed before, but then there are also things like this:

public interface ColorMarkerImplementation {
    int markCurrentWithColor(Color color);
}

If I understand it correctly, you would call the InputOutput to do this.  I
don't think it can work - there can be two threads writing to the OW at the same
time, and the only way to reliably associate something that was written to the
output is to pass it at the time of writing.  The one other way would be if
every write returned a LineContext, which doesn't sound like a great idea.

Is there something I'm not understanding here?
Comment 18 ivan 2007-03-28 21:41:30 UTC
This, the discussion on this IZ has shifted between spot fixes and 
a wholesale replacement. It's unclear to me where it stands now.
Can you please attach a summary with pointers to relevant specs?
Comment 19 _ tboudreau 2007-03-29 04:39:40 UTC
I've committed a revised IO API to openide/io2 on the branch io_api_rewrite -
it's based on what Milos sketched last fall.

Implementing it over core/output2 will be a bit of work yet - the mirror class
separation (i.e. final InputOutput delegates to the InputOutputImplementation
SPI class) makes things complicated to implement (though simpler for API users
and better for everyone in terms of being able to be evolved compatibly).

I took the step of turning a lot of the interfaces in Milos' sketch into
abstract classes and using the trampoline pattern so that the SPI consists only
of protected methods - i.e. even if someone could get hold of an instance of one
of the SPI classes, there is no way to call it.  It's a little insane, but it's
also a good way to bolt your API through the floor with cast iron bolts - it's
basically impossible to abuse this way.

Part of the idea is that it allows for private API contracts - i.e. you could
call InputOutput.getLookup().lookup(InlineIoFilter.class) and if it returns
non-null, some implementation of the output window could allow for inline IO
filtering, even if the IO API does not (currently) make any provision for such a
thing.

I'd like to see us solve this pile of enhancements somehow before 6.0 hits
feature freeze - that's not a lot of time.

The bottom line is that people want and need the things we're discussing here. 
It seems silly to leave them unimplemented until we can solve the world's I/O
problems.  But I'm willing to try to solve the world's problems if that's what
it takes.
Comment 20 ivan 2007-03-29 05:27:37 UTC
Thanks for the update. I'll check it out ASAP.
One issue I had earlier which didn't get resolved (perhaps even through
my own laziness) is ability to choose providers, perhaps through 
specifying a set of capabilities. Miloses proposal is an API that shunned
lookup, and I never figured how this choice could be made.

I think there is a good case to be made for user choice of providers.
Just recently Tor was shopping for a pty-based io so ruby could run
in interactive mode.
CDP allows the use of external terminals and consoles. Would be nice
to have an i/o provider that does that but works through this API.
Comment 21 t_h 2009-02-23 14:09:36 UTC
core-main #3234c129f8ca