|
|
|
|
|
|
| |
| |
|
|
|
|
| |
| |
|
|
Yesterday I was sitting at my PC, listening to Alisha's Attic and coding
in Java.
So... doing what I was doing 10 years ago, then. :-P
Some things change, others remain the same. Java ditched the Abstract
Window Toolkit and went with Swing. It's still absurdly over-complicated
and confusing. At least it appears to be very slightly better
documented. (Thankfully, NetBeans has a UI painter. Weirdly, it doesn't
let you specify alignment constraints or anything more sophisticated
then "put this button here". So if you resize the window, it's anybody's
guess what will happen.)
Figuring out how to do a tree view was fun. Fortunately, there's a
tutorial. Otherwise I would never have figured out that each tree node
is an object of class DefaultMutableTreeNode. (DefaultTreeNode won't cut
it, apparently.) I'm still waiting on why you need a JTree object
pointing to a DefaultMutableTreeModel object which /then/ points to a
DefaultMutableTreeNode representing the root of the actual tree being
displayed...
Just like in the old days, there are some very odd design decisions. For
example, you would /think/ that the tree would default to being
expanded. Failing that, you might guess that defaulting to being
collapsed would be reasonable. But in fact, Swing does neither. What it
/actually/ does is that _the root node_ defaults to being expanded,
while all other nodes default to being collapsed. Nice consistency
there. :-P
You would think there would be a method that says "expend this tree
node". Well, there isn't. Instead, what you have to do is create a "tree
path" object which describes the location of the tree node you want to
expand. Or, alternatively, you can use a row number. (Christ only knows
how /that/ actually works!) You might expect to find an "expand all"
method, with a matching "collapse all" method. You would be
disappointed. Yes, if you just want to expand all the nodes in the tree,
you have to actually manually write a for-loop to repeatedly call the
expand() method.
Also, depending on exactly which way you modify the tree, you may or may
not have to call reload() on the tree widget (not the tree model or the
tree root or the tree node you just changed) to make the change visible.
Otherwise the change only becomes visible if the window has to repaint
for some reason. No, hiding and revealing the window does not make it
repaint. But resizing it does.
Just like in the old days, things are still a bit buggy. For example,
Swing has a random feature where if some display text begins with the
token "<html>", then some but not all HTML formatting commands are
accepted. Of course, in real HTML, you'd mark chunks of stuff with tags,
and then use CSS to decide how to actually format that. Swing appear to
only understand raw HTML formatting commands. Naturally, the exact set
of commands supported is not documented anywhere.
The really buggy thing though is this: If you disable a widget, its
label goes grey. But if the widget uses HTML formatting, the text no
longer turns grey. Presumably because they haven't figured out how to
combine that with the HTML formatting. (Surely the solution is just to
apply a little alpha blending...)
There's a ticket for it on the bug tracker. But it's not marked as a
bug, but rather as a "feature request". (Yes, that's right. The software
working correctly in a certain special case is a "feature request", not
a basic expectation.) The priority is set to "very low". There are
thousands of replies, most of them containing elaborate Java source code
to work around the problem. People - THIS IS NOT HOW TO CREATE QUALITY
SOFTWARE! >_<
In other news, there appears to be no way to make NetBeans halt on
exceptions. Like, if an exception is thrown, I would like to halt the
debugger at the line where it was thrown, so I can inspect the program
state and, you know, /debug it/. But apparently no, you cannot do that.
Because technically, the exception is thrown, and then /caught/ by the
top-level exception handler. And handled exceptions "don't count". Never
mind the fact that Java's checked exceptions mentality /requires/ you to
catch all exceptions. Your code actually won't compile (and hence won't
run) if you don't do this. *sigh*
I have at least found out how to single-step Java code in the debugger
now. But that doesn't work at all with GUI code. You can't single-step
into an event handler that only runs when the user clicks a button. You
also apparently cannot set breakpoints - I can only presume because the
code runs in a thread other than the one the debugger was expecting.
Seriously, nobody thought to test that one?
In about two days of coding, I did manage to write my program, and have
it work. It's ages since I wrote a program with a real, native-looking
GUI. I'd forgotten how satisfying that is. (When it actually works!) But
now one of the program features has a small bug in it, which I am
seemingly powerless to track down. Time for the next iteration of my
prototyping...
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
On 02/04/2012 10:07 AM, Invisible wrote:
> Yesterday I was sitting at my PC, listening to Alisha's Attic and coding
> in Java.
>
> So... doing what I was doing 10 years ago, then. :-P
It's interesting, actually. I needed a parser. Since Java doesn't have
any parsing libraries, I started writing one. But I made the mistake of
trying to write it the way you'd write in in Haskell. Pro tip: DON'T DO
THAT!
You see, in Haskell, it's trivial to write a function that takes some
other function as its argument to do some key operation, or to implement
the continuation, or whatever. But in Java, that's wicked-hard. You'd
have to write an entire class for every "function" you want to pass as a
parameter. (The fact that "anonymous inner classes" even exist tells you
how badly some Java programmers want first-class functions...)
In Haskell, you'd write a parser combinator library. You have a couple
of primitive parsers, and then you have functions that construct more
sophisticated parsers from simpler ones. Trouble is, in Java every
"parser" has to be an entire class. And where in Haskell you can just
say "foo 1 2 3" and get a 1-argument function, in Java you must say
public class Foo extends Parser
{
private int arg1, arg2, arg3;
public Foo(int a1, int a2, int a3)
{
this.arg1 = a1;
this.arg2 = a2;
this.arg3 = a3;
}
}
This is before you even /implement/ the actual functionality of the
thing! In short, Java makes this kind of thing excruciatingly verbose.
Also, unlike Haskell, it's not designed to be used this way at all, and
so it doesn't optimise all this overhead away. I ended up writing a huge
mountain of very complex and buggy code that's a nightmare to understand.
So then I took a step back, and tried to write a parser the way a Java
programmer might do it. And you know what? The lexer is pretty simple.
You do something like
public class Lexer
{
private String buffer;
private int index;
public Lexer(String in)
{
this.buffer = in;
this.index = 0;
}
private char Peek() {return this.buffer.chatAt(this.Index);}
private void Next() {this.index++;}
private boolean EOF() {return this.Index < this.buffer.length();}
public Token GetNextToken() {...}
}
There's basically two types of token: Ones that consist of just one
special character, and ones that consist of a run of characters of a
certain type. In Haskell, you'd abstract out these patterns, but in Java
that's too painful. The single-character case is trivial to handle, and
there are only two types of character run to worry about, so the code
repartition isn't too bad.
Next, you need to parse the tokens into a parse tree. Now there's
several ways to do that. The way I tried to do it was Dijkstra's
shunting algorithm. In it's original form, it takes a list of tokens and
builds a Polish reversed list. But it's easy to modify it to build a
parse tree instead.
The original algorithm looks like
- Read token.
- If operand, output.
- If operator, push to the operator stack. (Possibly unstacking and
outputting some existing operators first.)
- At the end, unstack all remaining operators as above.
All you gotta do is keep the operands on a stack as well, and when you
unstack an operator, you pull its operands off the operand stack, make a
little expression tree out of it, and push that back to the operand
stack. At the end, there should be one operand left, which is your
expression tree.
The really tricky part is handling brackets and function calls. In fact,
I discovered that my parser fails for functions that take zero
arguments. It appears the only way to fix this is for the parser to
"know" how many arguments each function is supposed to take - which is a
dependence I really don't want to add. (The alternative is to hack
around the problem with status flags, which is a bit messy.)
Apart from empty function calls, my parser works. Trouble is, every
possible operator token becomes a whole class, because every token
behaves differently. (In particular, most tokens need to be turned into
/expressions/ at some point.) So you end up with a rather large
profusion of classes!
I ended up with 12 concrete token classes, plus about 5 abstract classes
to handle shared functionality. Add on one class for the parser (which
manages the two stacks), one class for the lexer, and one class for the
parser exception (in Java, an exception is a class) and we have a grand
total of 20 classes to implement this simple little parser. And it
doesn't even give decent error messages!
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
Invisible escreveu:
> It's interesting, actually. I needed a parser. Since Java doesn't have
> any parsing libraries
LOL
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
>> It's interesting, actually. I needed a parser. Since Java doesn't have
>> any parsing libraries
>
> LOL
What I obviously /meant/ was that it doesn't come with one
out-of-the-box. Sheesh...
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
Em 03/04/2012 05:17, Invisible escreveu:
>>> It's interesting, actually. I needed a parser. Since Java doesn't have
>>> any parsing libraries
>>
>> LOL
>
> What I obviously /meant/ was that it doesn't come with one
> out-of-the-box. Sheesh...
well, neither is parsec. Still, you didn't go on writing your own, did
you? :p
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
>>>> It's interesting, actually. I needed a parser. Since Java doesn't have
>>>> any parsing libraries
>>>
>>> LOL
>>
>> What I obviously /meant/ was that it doesn't come with one
>> out-of-the-box. Sheesh...
>
> well, neither is parsec. Still, you didn't go on writing your own, did
> you? :p
When you install Haskell, Parsec /is/ included in the default install.
(Unless you go with one of the more obscure Haskell implementations.)
When you install the normal JDK bundle, it doesn't include a parsing
library.
OTOH, when you install Haskell, you don't get any GUI capabilities, but
with the JDK you do. They just come with different sets of libraries,
that's all.
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
On 02/04/2012 10:07 AM, Invisible wrote:
> Just like in the old days, things are still a bit buggy.
Wait - you implemented generics but didn't implement generic array
creation??!
*facepalm*
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
Gotta love the way NetBeans displays stdout and stderr in different
colours, but the two streams don't actually line up!
(For example, if the program throws an exception, the exception text
appears a couple of lines /above/ the line of output where the exception
was actually thrown...)
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
On 4/3/2012 6:41, Invisible wrote:
> When you install the normal JDK bundle, it doesn't include a parsing library.
Now, grasshopper, do you understand why every configuration file everywhere
in any Java-based system is based on XML or property files?
--
Darren New, San Diego CA, USA (PST)
"Oh no! We're out of code juice!"
"Don't panic. There's beans and filters
in the cabinet."
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
On 06/04/2012 03:12 AM, Darren New wrote:
> On 4/3/2012 6:41, Invisible wrote:
>> When you install the normal JDK bundle, it doesn't include a parsing
>> library.
>
> Now, grasshopper, do you understand why every configuration file
> everywhere in any Java-based system is based on XML or property files?
Oh, well, that's almost an /advantage/. Why invent yet another file
format when you can use an existing well-supported one?
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
|
|