 |
 |
|
 |
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
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
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
On 4/6/2012 2:02, Orchid Win7 v1 wrote:
> 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?
Because XML sucks for that purpose, because it was never designed for that
purpose.
--
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
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
>>> 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?
>
> Because XML sucks for that purpose, because it was never designed for
> that purpose.
So what would you use?
Personally, I'm somewhat partial to JSON. Unfortunately, the rest of the
world seems to like the horrifyingly awful YAML...
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
I almost feel like I'm going mad. I'm trying to use the NetBeans
debugger, and... well... it doesn't appear to /work/.
I'm single-stepping through the code, and half the time the local
variables window doesn't even remotely correspond to the code I'm
actually looking at. I step over a line that says id++, and the id field
remains stubbornly fixed at its previous value. Even though I just
god-damn /watched/ the increment instruction execute! WTF?!
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
On 4/7/2012 11:01, Orchid Win7 v1 wrote:
> So what would you use?
I would use something specific to what I'm configuring, if possible. I don't
want my makefiles, my database schema, my network packet layout, and my
description of my UI to all be written in the same language.
--
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 4/7/2012 1:52 PM, Orchid Win7 v1 wrote:
> I almost feel like I'm going mad. I'm trying to use the NetBeans
> debugger, and... well... it doesn't appear to /work/.
>
> I'm single-stepping through the code, and half the time the local
> variables window doesn't even remotely correspond to the code I'm
> actually looking at. I step over a line that says id++, and the id field
> remains stubbornly fixed at its previous value. Even though I just
> god-damn /watched/ the increment instruction execute! WTF?!
Often a result of the "id" being tracked having been from "someplace
else", like a different function, and thus the "local" copy is changing,
or the copy within *this instance of the function call*, but not the one
that is actually being tracked (which was the last time it was called,
or from a different function or... who the hell knows). Basically,
tracking is context insensitive, so you are not tracking which ever
version of the variable you are currently stepping through, as expected,
you are tracking one from a different context.
And, yeah, its a damn pain in the ass that this happens, but sort of
understandable (especially when its in a library, or the like, where you
might have 3-4 instances running in memory, so you actually could, in
principle, have 3-4 different copies of the same variable, all of them
changing independently). What might be nice is a way to identify which
thing the variable really belongs to, like contextual highlighting
**if** its the one local to what you are looking at. Or, if you want to
live dangerous, and be more confused, you could just have it track which
ever one "is" being looked at... I think the "highlight the one you are
currently looking at" version, would be better, since you can tell what
you are actually dealing with, and not confuse the current one with one
that belongs to something else, or some other instance. A combination
could be helpful in some cases too, but then you kind of need to have
some way to tell which of these is the "local" one:
int id 234
int id 10
int id 135435
Other than that one happens to be changing. And, there may be cases
where you do need to see what all of them at doing, even with the same
name, so:
main: int id 234
sub1: int id 10
print: int id 135435
Its just, doing that isn't something some debuggers just don't handle
right (in at least one case, setting the watch on it worked, the first
pass through, but the second time the function was called, it was no
longer tracking the same variable. Why? Because the original variable
didn't exist, just a new one, which happened to have the same name, and
so it couldn't "track" changes to something that wasn't there any more.
Yeah, doesn't make a lot of sense, from a debugging standpoint, to me
either, but that is what the bloody thing was doing, tracking the "last
value" the original one had, even thought it was gone, and I wanted to
see what the new copy was doing. I don't remember how/if I got around
the bloody problem at the time.
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
>> I'm single-stepping through the code, and half the time the local
>> variables window doesn't even remotely correspond to the code I'm
>> actually looking at. I step over a line that says id++, and the id field
>> remains stubbornly fixed at its previous value. Even though I just
>> god-damn /watched/ the increment instruction execute! WTF?!
> Often a result of the "id" being tracked having been from "someplace
> else", like a different function, and thus the "local" copy is changing,
> or the copy within *this instance of the function call*, but not the one
> that is actually being tracked (which was the last time it was called,
> or from a different function or... who the hell knows). Basically,
> tracking is context insensitive, so you are not tracking which ever
> version of the variable you are currently stepping through, as expected,
> you are tracking one from a different context.
Thing is, most of the time when you jump to another function, the local
variables display updates to reflect that you're now in a different
scope. But, about 30% of the time, /it doesn't update/. So you're now
looking at a completely irrelevant bunch of data while single-stepping
your code.
I haven't seen any other debugger fail in this way. I've seen debuggers
where you can't /see/ all the local variables, only the once you've
configured to "watch". But I haven't seen a debugger where the watched
variables don't actually frigging update at random. I can only imagine
it's a bug. A bug... in the debugger. Now that's meta.
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
On 07/04/2012 11:01 PM, Darren New wrote:
> On 4/7/2012 11:01, Orchid Win7 v1 wrote:
>> So what would you use?
>
> I would use something specific to what I'm configuring, if possible. I
> don't want my makefiles, my database schema, my network packet layout,
> and my description of my UI to all be written in the same language.
Um... why?
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
On 4/8/2012 1:53, Orchid Win7 v1 wrote:
> On 07/04/2012 11:01 PM, Darren New wrote:
>> On 4/7/2012 11:01, Orchid Win7 v1 wrote:
>>> So what would you use?
>>
>> I would use something specific to what I'm configuring, if possible. I
>> don't want my makefiles, my database schema, my network packet layout,
>> and my description of my UI to all be written in the same language.
>
> Um... why?
Because that means you're using the wrong language for all of them. It's
inefficient, confusing, and full of senseless markup that's irrelevant to
the problem at hand.
How would you feel if someone said "We're replacing Haskell syntax with XML"?
--
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 4/8/2012 1:53, Orchid Win7 v1 wrote:
> Thing is, most of the time when you jump to another function, the local
> variables display updates to reflect that you're now in a different scope.
> But, about 30% of the time, /it doesn't update/.
I'm guessing either your debugger is bugged, or your compiler is doing a
good job of inlining functions.
--
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 4/8/2012 1:53 AM, Orchid Win7 v1 wrote:
> I haven't seen any other debugger fail in this way. I've seen debuggers
> where you can't /see/ all the local variables, only the once you've
> configured to "watch". But I haven't seen a debugger where the watched
> variables don't actually frigging update at random. I can only imagine
> it's a bug. A bug... in the debugger. Now that's meta.
Thing is, I have seen it. I don't remember the context, but I think it
had something to do with "when" and "how" I added the watch for that
variable. Something to do with creating it "during" the execution of the
function, instead of as a global watch, tied to 'all' copies of that
specific function's variables, or something.
And, while I can't be sure, I think it was one of the higher end
debuggers. But, yeah, it could have been a bug too.
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
>>> I would use something specific to what I'm configuring, if possible. I
>>> don't want my makefiles, my database schema, my network packet layout,
>>> and my description of my UI to all be written in the same language.
>>
>> Um... why?
>
> Because that means you're using the wrong language for all of them. It's
> inefficient, confusing, and full of senseless markup that's irrelevant
> to the problem at hand.
>
> How would you feel if someone said "We're replacing Haskell syntax with
> XML"?
If you're saying "XML is not a good configuration language" then yeah,
OK, I can go with that.
If you're saying "it's wrong to use the same language for more than one
task" then, uh, what the hell...?
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
On 4/9/2012 1:47, Orchid Win7 v1 wrote:
> If you're saying "it's wrong to use the same language for more than one
> task" then, uh, what the hell...?
No, I'm saying it's probably a bad idea to use the same language for *every*
task, regardless of how good a fit it is.
--
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 11/04/2012 01:46 AM, Darren New wrote:
> On 4/9/2012 1:47, Orchid Win7 v1 wrote:
>> If you're saying "it's wrong to use the same language for more than one
>> task" then, uh, what the hell...?
>
> No, I'm saying it's probably a bad idea to use the same language for
> *every* task, regardless of how good a fit it is.
OK. But I'm sure there's quite a lot of configuration files that all
have roughly the same characteristics, and it would be nice to not have
to learn /another/ language every single time I want to configure something.
(Heck, almost every Windows program is configured by the Registry,
right? ;-) LOL...)
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Le 11/04/2012 10:46, Invisible nous fit lire :
>>
>> No, I'm saying it's probably a bad idea to use the same language for
>> *every* task, regardless of how good a fit it is.
>
> OK. But I'm sure there's quite a lot of configuration files that all
> have roughly the same characteristics, and it would be nice to not have
> to learn /another/ language every single time I want to configure
> something.
Configuration files is one task.
Reinventing the wheel has only one purpose: the ego. (well, the Ego,
with an uppercase).
When an operating system or a usual library provides a descent solution,
it is good to use it.
When there is no de facto solution, then individual solutions flourish.
But when the operating system evolved with a changing solution, havoc
are around. Windows move from *.ini files to registry in slow motion.
But configuration files is only one task.
The details of my bank's operations is not a configuration (or you are a
pervert with a twisted common sense), neither is a digital comic or a
word-processor document.
Java has a strength, which is also its weakness: usability on different
system. (it's not just portability)
It also means that an operating system specific solution cannot be used
(unless the Virtual Machine made provision for that in its specification)
That's also means that a pure Java program cannot use directly a Direct
X interface, nor the native widgets of the graphical interface. Of
course, you can, but it soon stop being runnable anywhere if you do not
write convoluted libraries to handle the cases when the direct mapping
is not available.
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|
 |