JGears on Eclipse Update Site
For the last couple of months I have been working on getting our JGears for Eclipse products ready for "shipping". I say "shipping" because unlike previous releases of JBuilder, which have always had a physical disc in a box that is literally shipped to customers and resellers, we are selling our JGears through our update site. Anybody can download the JGears, although the functionality will only be enabled with a valid license.
At any rate, we are using the Eclipse Update Site technology for installing the JGears. I learned a lot about it, although I suspect there is still a lot that I don’t know. When working on the project, I did a Google search for articles about Eclipse update sites, I was surprised that a very simple blog I had written came up on the first page of search results. Actually, "dismayed" is a more accurate description of how I felt. That blog was quite basic, and the fact that it was relatively high in the search results only served to emphasize the lack of information and documentation in this area. It’s a problem I have with Eclipse and Open Source software in general — there are lots of dedicated developers writing neat, powerful software, but the documentation is often lagging, if it is there at all. Lest that be misinterpreted, let me state that I am not against Open Source software, not at all. It just gets challenging to work with at times.
I digress. I am going to try to fill in the Eclipse update site information gap a little, and hopefully with the correct information. In a series of blogs, until I run out of steam, I will cover some of the issues we ran into.
Dependencies
One issue we ran into is how the Update Manager resolves dependencies. This is an issue in particular because our update site contains both the JGears and JBuilder 2007 itself, so that JBuilder customers can get updates to JBuilder. Keep in mind that the JGears are essentially JBuilder 2007 functionality split up into different parts. When an Eclipse user wants to download a JGear, we want to be sure that the user only downloads the files necessary for the JGear, and not any of the JBuilder files. Sounds simple, right? Following is a simplified example that shows how it gets tricky. This particulars of this scenario are made up to demonstrate the point.
- JBuilder Enterprise feature.xml includes the Optimizeit feature.xml:
...
<includes id="com.borland.optimizeit"
version="1.0.2"
optional="true"/>
... - JGear Performance feature.xml requires the Optimizeit feature.xml:
…
<requires>
...
<import feature="com.borland.optimizeit" version="1.0.2"/>
...
</requires>
... - User goes to update site, selects JGear Performance, then clicks on the Select Required button.
At this point, if you look at what is selected, you would expect, among other things, that JGear Performance and Optimizeit are selected. Instead, based on your site.xml, what you may see selected are JGear Performance and JBuilder Enterprise! So if the user proceeded, she would not only download JGear Performance, but all of JBuilder Enterprise.
Why does this happen? Because when the user presses the Select Required button, Eclipse apparently goes through the site.xml, looking for the first thing that resolves the Optimizeit requirement1 . In this case, because JBuilder Enterprise includes Optimizeit, JBuilder Enterprise resolves the requirement, and it is selected. Unfortunately, everything else in JBuilder Enterprise and its dependencies in turn are also selected, so the user ends up downloading a lot more than she needs for the JGear.
Possible Solutions
- One solution is to simply change the JGear Performance feature.xml to include com.borland.optimizeit, instead of requiring it. The trade-off is that you are now including one feature in more than one feature. That seems wrong to me, but in certain cases it is unavoidable.
- Another solution is to change the JBuilder Enterprise feature.xml to require com.borland.optimizeit instead of including it. The drawbacks to this approach are:
- Requires are less flexible in that they don’t have platform filters. Optimizeit is currently not available on the Mac. If we include it in a feature, we can say only include if the platform is Windows or Linux. If we don’t want the JGear Performance feature including Optimizeit to ship on a Mac, then fine, but if we do want the rest of JGear Performance to be available on the Mac, we can’t do that with a requires.
- Requires are also less flexible in that they are always required, as the name implies. You can’t make them optional. Generally, I like to make included features optional. That way the user can disable them or not even download them if they are something he is not going to use.
- As far as I know, PDE builds will only build items included in features. In this example, it means that Optimizeit would have to be built separately. Which may or not be a bad thing in the particular case.
- An ugly solution is to reorder the site.xml. If we put JBuilder Enterprise at the bottom of the site.xml, then when the dependencies are resolved, the Optimizeit feature will be found before the JBuilder Enterprise feature, and since it satisfies the requirement, JBuilder Enterprise will not be selected.
With JBuilder and the JGears, the number and mix of dependencies than the simple example above. Sometimes the solution uses all 3 of the above techniques, even #3, which I really dislike, but was sometimes unavoidable.
1 I say "apparently", based on the results of my limited testing in this area. I did not find any documentation describing this as intentional behavior, nor did I debug the code to confirm it.
Share This | Email this page to a friend
Posted by Charles Overbeck on September 7th, 2007 under Eclipse, JBuilder | Comment now »StarTeam Components for JBuilder 2007 Visual Editor
One weakness of JBuilder 2007/Eclipse 3.2 is that users cannot modify the Visual Editor (VE) palette dynamically via the UI, like you can with JBuilder 2006. Even though you can add any bean in the Visual Editor via Choose Bean, it’s nice to be able to drag them off a palette.
In JB2007 you have to write a plug-in to get new things to appear on the palette. So as an experiment, as a learning exercise, and as a request from a member of StarTeam Customer Engineer team, I have written a plug-in that adds the StarTeam visual components to the VE palette. I have uploaded it here.
It’s not as complete as it could be. If there’s enough interest, I might try to improve it. Or anybody who is interested can use it as a basis to add their own components of interest to the palette.
Here are a couple of links that I referred to in writing the plug-in:
- Extending the Visual Editor Tutorial: Enabling support for a custom widget
- Extend VE to build a ULC GUI Builder
Share This | Email this page to a friend
Posted by Charles Overbeck on June 8th, 2007 under Eclipse, JBuilder | 2 Comments »JBuilder 2007 Code Assist / JBuilder 2006 CodeInsight
One of the annoying differences for me between JBuilder 2006 and JBuilder 2007 has been one way Content Assist (JB2007/Eclipse terminology) functions in comparison to CodeInsight (JB2006 terminology). In JB2006, when you invoke CodeInsight in the middle of an expression and select a method, JB2006 overwrites the relevant part of expression, leaving you with a syntactically correct expression. In JB2007, when you do the same thing, it inserts the selected method, leaving you with a syntactically incorrect expression.
For example, say you have this following code snippet:
String myVar = stringVar.toUpperCase();
Let’s say you want myVar to contain the lower case value of stringVar, but you forget the exact name of the function (or you don’t want to type it in yourself). So what I do is put the cursor after the "o" in "toUpperCase", then press Ctrl+Space, then arrow to the toLowerCase method, and press Enter. In JB2006 you end up with:
String myVar = stringVar.toLowerCase();
That’s perfect. But in JB2007, you end up with:
String myVar = stringVar.toLowerCase()UpperCase();
Ugh! Now you have to go manually delete the UpperCase() part.
But it turns out that is only the JB2007 default behavior. Bill J. from the R&D team pointed this out to me the other day (and he may have got it from the newsgroups, so apologies in advance if I am stealing somebody else’s discovery without propery attribution). At any rate, in JB2007, if you hold down the Ctrl key before pressing Enter, it will overwrite, not insert. What’s more, you can change the default behavior. Select Window | Preferences | Editor | Content Assist | Completion Overwrites, and the default behavior will overwrite, just like JB2006. And regardless of your default, you can always toggle the behavior with the Ctrl key.
Share This | Email this page to a friend
Posted by Charles Overbeck on February 27th, 2007 under Eclipse, JBuilder | 3 Comments »JBuilder 2007 Usability Survey
The JBuilder team would love to get feedback on how were doing with the usability of JBuilder 2007. We’ve created a really short survey (12 questions) to get that feedback, it’s here: http://www.surveymonkey.com/s.asp?u=109923283362
Share This | Email this page to a friend
Posted by Charles Overbeck on February 15th, 2007 under JBuilder | Comment now »Better Debugging Tip with JBuilder 2007
In an earlier blog I complained about JBuilder 2007/Eclipse not having the "Quick Step into" feature that is in JBuilder 2006. Well, it turns out I was wrong. In doing his Making the switch to JBuilder 2007 video, Ken Chan found out how. Not only does JB2007 have the feature, it’s little more powerful than it is in JB2006.
Here is a very simple example demonstrating the feature:
public class Main {
public static void main(String[] args) {
print(sayHello("Charles".toUpperCase()));
}
private static String sayHello(String name) {
return "Hello " + name;
}
private static void print(String message) {
System.out.println(message);
}
}
Say you set and have hit the breakpoint on the print(sayHello("Charles".toUpperCase()));. If you do "Step into", you will first go into String.toUpperCase(). You come back from the method, step into again, and you go into sayHello. You come back from that method, step into again, and now you are in the print method.
Sometimes you may want to do that. But what if you want to go directly to print? In JB2006, you press Shift+F7 and you go straight to the first line of the print() method.
In JBuilder 2007, the keystroke is, I just found out, Ctrl+F5 (the functionality is also available via the context menu). But the keystroke behaves a little differently. (In JB2006, it doesn’t matter where the cursor is. In JB2007, the cursor has to be positioned somewhere in "print". In other words, in the bold part of the statement: print(sayHello("Charles".toUpperCase()));.
The requirement to position the cursor provides the extra power in JB2007. Let’s say you do want to step into sayHello, but you don’t want to step into String.toUpperCase(). Position the cursor anywhere in sayHello portion of print(sayHello("Charles".toUpperCase()));, and press Ctrl+F5. That’s the extra functionality. In JB2006, you can step into the outermost function, or into the innermost function. In JB2007, you can start stepping into any nested function.
Thanks to Ken for pointing this out.
Charles
Share This | Email this page to a friend
Posted by Charles Overbeck on February 8th, 2007 under Eclipse, JBuilder | Comment now »Java, Serializable, and Externalizable, Part II
In response to my last blog on Externalizable, a comment by Benoit pointed out some issues that I forgot to cover.
- You need to call
super.writeExternal()andsuper.readExternal()if you want fields from the base class(es) initialized too. It doesn’t matter if where you invoke the super method, as long as it is done in the same place:public void writeExternal(ObjectOutput out) throws IOException { out.writeUTF(stringField); super.writeExternal(out); // Done after writing stringField } public void readExternal(ObjectInput in) throws ClassNotFoundException, IOException { in.readUTF(stringField); super.readExternal(in); // Then also do after reading stringField }We could have made the super calls the first line in each method, and that would work too, as long as we’re consistent.
- For writing out arrays and Collections, we explicitly write out the length of the array/collection and then the indvidual items.
public class MessageWithArray implements Externalizable { private String [] items; ... public void writeExternal(ObjectOutput out) throws IOException { out.writeInt(items == null ? -1 : items.length); if (items != null) { for (String item : items) { out.writeUTF(item); } } } public void readExternal(ObjectInput in) throws ClassNotFoundException, IOException { int len = in.readInt(); if (len >= 0) { items = new String[len]; for (int i = 0; i < len; i++) { items[i] = in.readUTF(); } } }Note how we check to see if
itemsis null, and if it is, we write out-1. When we read it in and see a-1, we don’t bother to instantiate the array. You would only add this check if you know thatitemscould be null. If you always ensure that it’s not null, then you don’t need to worry about it.Also, the
writeExternal()will fail if any of of the array elements arenull. Just like above, if this class never allows null elements in theitemsarray, then it’s not an issue. If it does, then we need to modify the code.Here’s how the code would look like if
itemsis aListinstead of an array:public class MessageWithList implements Externalizable { private List<String> items; ... public void writeExternal(ObjectOutput out) throws IOException { out.writeInt(items == null ? -1 : items.size()); if (items != null) { for (String item : items) { out.writeUTF(item); } } } public void readExternal(ObjectInput in) throws ClassNotFoundException, IOException { int len = in.readInt(); if (len >= 0) { items = new ArrayList<String>(len); for (int i = 0; i < len; i++) { items.add(in.readUTF()); } } } - Fields that aren’t arrays/collections and that can be null also need to be handled with care:
public class MessageWithNullField implements Externalizable { private String neverNullField; private String sometimesNullField ... public void writeExternal(ObjectOutput out) throws IOException { out.writeUTF(neverNullField); boolean isNull = (sometimesNullField == null); out.writeBoolean(isNull); if (!isNull) { out.writeUTF(sometimesNullField); } } public void readExternal(ObjectInput in) throws ClassNotFoundException, IOException { neverNullField = in.readUTF(); boolean isNull = in.readBoolean(); if (!isNull) { sometimesNullField = in.readUTF(); } }
In terms of performance of the actual serialization/deserialization, it’s something we never researched, since we had to use Externalizable due to obfuscation issues. But I just looked around a little on the web, and it appears that Externalizable should be faster, because Serializable depends on reflection, and will typically write out more.
Finally, I am not aware of any IDE tool that does all the work to convert from Serializable to Externalizable. The way I do it using JBuilder 2007/Eclipse, is to just change Serializable to Externalizable in the editor, then use QuickFix (Ctrl+1) to add the unimplemented methods. At that point though I end up writing method implementation by hand. Certainly a wizard or something would be nice. It’s something to think about for the future.
Share This | Email this page to a friend
Posted by Charles Overbeck on February 2nd, 2007 under Java | Comment now »Java, Serializable, and Externalizable
One of the features in JBuilder 2006 is peer-to-peer. JBuilder 2006 allows users to exchange messages, edit a file remotely, debug remotely, etc. (Yes, JBuilder 2007 also has peer-to-peer, but the main purpose of this particular post is to discuss a serialization issue we first discovered in implementing peer-to-peer in JB2006). The actual sending of bytes from one machine to another in JB2006 is done using JGroups. Without going into too much detail, the way you send a message (the bytes) from one machine to another with JGroups is via Java serialization. You might have a class like this:
public class PeerMessage implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private final String message;
private final long id;
public PeerMessage(String message, long id) {
this.message = message;
this.id = id;
}
public long getId() {
return id;
}
public String getMessage() {
return message;
}
}
You then create instances of the PeerMessage class, pass them to JGroups, which sends them to another machine. The other machine deserializes, and processes the PeerMessage instance, e.g., perhaps by displaying PeerMessage.getMessage() in a chat window.
When we first implemented this in R&D, after ironing out some kinks, it was pretty much working. Developers could connect with other developers. Then Integration started delivering builds to QA, and we found that some instances of JBuilder could only successfully communicate with certain instances of JBuilder but not with others. We soon figured out what was going on. Our Integration department delivers both obfuscated and non-obfuscated builds to QA. The pattern was that obfuscated builds could only communicate with other obfuscated builds, and non-obfuscated builds could only communicate with non-obfuscated builds.
Does anybody see why the above, simple little class, is the problem? Well if you don’t, the solution follows. And, as we will see, the solution also produces less data when serializing objects, which is certainly a very important benefit when you are sending data over a network.
What does the default Java serialization do? It essentially writes out the fully qualified class name, the names and types of non-transient fields, and the values of the fields. When you deserialize, it instantiates the class using that information. (I am probably over-simplifying here. For purposes of this discussion though, it’s all we need to know. If you’re really interested, go check the Sun website for more details).
What does Java obfuscation do? It can rename package names, class names, method names, and field names. Most obfuscators give you control over what you want renamed. Typically you don’t want anything public or protected renamed; otherwise your API names would change.
So what happens when the above class is obfuscated? The fields message and id get renamed, probably to something cryptic like a and b (the getMessage() and getId() method names remain unchanged because they are public).
So when a message is sent from a non-obfuscated JBuilder to obfuscated JBuilder:
- Non-obfuscated JBuilder serializes the
PeerMessageinstance, writing out, among other things, the field namesidandmessage - Obfuscated JBuilder attempts to deserialize the
PeerMessage. It doesn’t work! Obfuscated JBuilder has a classPeerMessagewith fieldsaandb, but the serialized object has fieldsidandmessage.
Arguably we could have lived with this. We only ship obfuscated builds to customers, and the obfuscated builds were able to talk to each other. Although even that would get dicey for any updates — we would have to make sure all the relevant classes were obfuscated the exact same way, e.g., the field message was always renamed to a, or then different obfuscated versions may not work with each other. And it still made sense to fix it so developers could debug from their non-obfuscated builds while communicating with obfuscated builds. And, as I mentioned earlier, the solution produces smaller serialized objects, which should improve network performance.
The solution we came up with is to use an interface I was personally unfamiliar with, java.io.Externalizable. It extends Serializable. The difference is that you write the serialization and deserialization yourself. Here is the class PeerMessage2, with differences in bold:
import java.io.*;
public class PeerMessage2 implements Externalizable {
private static final long serialVersionUID = 1L;
private String message; //No longer final!
private long id; //No longer final!
public PeerMessage2() { //New, parameterless constructor
}
public PeerMessage2(String message, long id) {
this.message = message;
this.id = id;
}
public long getId() {
return id;
}
public String getMessage() {
return message;
}
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
id = in.readLong();
message = in.readUTF();
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeLong(id);
out.writeUTF(message);
}
}
With Externalizable, what is essentially written out is the fully qualified class name, and whatever you specify in writeExternal(). (Again I may be over-simplifying here, but it’s good enough to understand this problem). No field names, no field types. That is why a serialized PeerMessage2 is smaller than a serialized PeerMessage. I wrote a simple test, creating a PeerMessage object with "Hello" and 1 as its parameters, and serialized it to a file. I then created a PeerMessage2 object with the same parameters and serialized it to a file. The serialized PeerMessage instance was 105 bytes; the serialized PeerMessage2 was 72 bytes. Thats a difference of 33 bytes, making PeerMessage 45% larger than PeerMessage2, or making PeerMessage2 31% smaller than PeerMessage, depending on how you want to skew the statistics. Of course those numbers will vary according to the number of fields in the class, the length of the field names, and the size of the fields’ data. But the bottom line is that smaller serializations are produced.
And what about the obfuscation issue? It’s no longer an issue. Field names are no longer written out, just the field data is. So when the readExternal is executed, it doesn’t matter if the fields’ names are message and id, or if they are a and b. Whatever they are, the data gets assigned to them.
Unfortunately, there are some drawbacks to using Externalizable:
- The way
readExternal()works is that an instance of the class is created invoking a public, parameterless constructor. You then fill in the fields’ values of the instance inreadExternal(). Therefore, you must declare a public, parameterless constructor. I don’t like exposing that.PeerMessagecan only be instantiated by invoking the constructor with parameters. I know the fields in that class will be initialized. WithPeerMessage2the object can be created without initializing the fields. - Because of the the requirement of parameterless constructor, and because you have to assign fields values in
readExternal(), you can no longer make your fieldsfinal. I likefinalfields. It’s a commonly recommended practice. As Joshua Bloch’s Effective Java book says about classes, "Favor immutability". Yeah, you can still avoid having setters, so nobody from the outside can change the instance, but still…
But despite the drawbacks, Externalizable is certainly very useful in certain cases.
Charles
Share This | Email this page to a friend
Posted by Charles Overbeck on January 24th, 2007 under Java | 1 Comment »Basic Regular Expressions
I got an e-mail about my blog on JDK 5 features asking for details on how to replace StringBuffer with StringBuilder in a large codebase. I am going to use it as an opportunity to talk a little about regular expressions, even though:
- Many, if not most of you, reading this probably already know all about regular expressions. If you are able to sneer at this post as too remedial, then good for you. And I don’t mean that sarcastically. I think having even a cursory understanding of regular expressions can dramatically improve your productivity, both in moving around in your IDE, and in the code you write.
- Regular expressions are probably overkill for this particular problem. But hey, I think I can still squeeze something useful out of it.
Tools
Here are two indispensable "tools" for working with regular expressions:
- The book Mastering Regular Expressions, 2nd Edition, by Jeffrey Friedl. If you using them for the first time, it explains everything. If you are experienced with them, it’s a great reference. If you haven’t already, you could just get the book and stop reading the rest of this blog. But I’ll continue anyway.
- The RegEx Coach lets you try out all your regular expressions interactively. It is a standalone program.
I also see there is a Java-specific RegEx book, Java Regular Expressions: Taming the java.util.regex Engine. It has good reviews on Amazon, but I have not had a chance to read it.
There is also an Eclipse plugin for regular expressions, Regular Expression Tester. I tried it during the JBuilder 2007 development phase and it looked promising. The neat thing about it is that when you copy a regular expression and paste it into your Java code, it will add the extra backslashes (Background: Regular expressions, just like Java, use the backslash as an escape character. Thus when you code a regular expression in Java, you have to escape the escape character. You might end up having a lot of them in a particular expression, and it’s easy to miss escaping one backslash). There was something that bothered me about it though, so I ended up uninstalling it. I think it took over a keystroke I commonly use, but I don’t recall for sure. I’ve installed so many different plugins over the last year, so my memory is hazy. I just remember being very busy and deciding I didn’t have time to figure out the issue. I ended up uninstalling it. I’ll have to give it a try again.
Replacing StringBuffer with StringBuilder
As I mentioned in my previous post, I went through the JBuilder codebase and replaced all instances of StringBuffer with StringBuilder. I don’t recall how many I ended up replacing, but I believe it was at least in the hundreds. Now I could of just gone into the IDE and simply done a global search and replace of the literal string "StringBuffer" with the literal string "StringBuilder". But I didn’t want to do that in case we were using StringBuffer anywhere as a non-initialized field, because there might be threading issues, or if we were using it anywhere as a parameter to a method. Or any other cases that I can’t think of. Basically I wanted to change all lines of the type:
StringBuffer sb = new StringBuffer();
with
StringBuilder sb = new StringBuilder();
The problem with a literal search/replace is of course that the variable may not always be called sb. We can easily handle that with a regular expression. All of the following can be entered in the JBuilder 2007/Eclipse Search | File | File Search. Make sure you have the Regular expression checkbox selected. Tip: In that dialog, if you press Ctrl+Space in the Containing text field, you get Code Assist for regular expressions.
So lets start out with a simple regular expression:
StringBuffer (w+) = new StringBuffer();
Let’s look at the new "(w+)". The w is regular expression construct standing for an word character (0-9, a-z, A-Z, _). The + means 1 or more consecutive instances of the preceding. Our regular expession will find any line instantiating a StringBuffer variable, whose name consists of 1…n word characters. The parentheses around the w+are not literal. They are for grouping that part of the match. We will need the group when we do the replace. Since the parenthesis is a special character in regular expressions, when we want to look for a parenthesis in a string, we need to escape it in the expression. That’s why the expression ends with (). It means we are looking for the literal left parenthesis and the literal right parenthesis.
One important thing to note is that we could further refine the expression so that it only searches for legal Java variables. A Java variable cannot begin with a numeric. A Java variable has a maximum number of characters. Our current expression does not catch either of those cases. But we don’t worry about it. My codebase compiles. That means I don’t have any illegal Java variable names. It’s one of the points that I took from Mastering Regular Expressions. You can base your expression on what you know about what you are searching. In this case I know I am searching error-free Java code.
To do the replace, from the dialog, we click on Replace instead of Search. When our first match is found, we enter this in With portion of the Replace dialog, making sure that Regular Expression is checked:
StringBuilder $1 = new StringBuilder();
The $1 refers to (w+) from our search expression. It means whatever the collection of characters that the expression matched in that group should be part of the replace. For example, if the variable name was foo in the original statement, then it will continue to be foo in the replaced statement.
What’s wrong with this regular expression? A few things:
- It doesn’t work if the user passes a parameter to the
StringBuilderconstructor. - It doesn’t work if there are any extra spaces between any of the tokens.
- It doesn’t work if there is an embedded tab between any of the tokens
- It doesn’t work if the statement spans two lines.
Let’s tackle the argument(s) to the constructor first. To handle it, we can use this expression:
StringBuffer (w+) = new StringBuffer((.*));
We’ve added the (.*). The parentheses are there for grouping. The . means match any character. The * means 0 or more instances. So what we are looking for after the left parenthesis is any number of characters, including 0 characters to handle the no-argument constructor case, followed by a right parethesis, followed by a semicolon. This should handle constructors like new StringBuffer(), new StringBuffer("whatever"), new StringBuffer(16), and new StringBuffer(methodThatReturnsAStringOrInt()).
If we run this, we have to modify our replace expression, because we now have two groups:
StringBuilder $1 = new StringBuilder($2);
In general, you want to be careful when using .* in a regular expression, because it matches anything. Again, we could come up with a more refined expression, but this one serves our purpose.
What do we do about the potential extra spaces, tabs, and/or newlines? For that we can use s, which matches a whitespace character. Whitespace is typically considered to be the space, tab, carriage return, and line feed characters, although its meaning can vary depending on the specfic regular expression implementation. So to handle either a space or a tab before the variable:
StringBuffers(w+) = new StringBuffer((.*));
That will only handle one tab or space. What if there are two or more? We just add the +:
StringBuffers+(w+) = new StringBuffer((.*));
Now we put the s+ between all the tokens:
StringBuffers+(w+)s+=s+news+StringBuffer((.*));
Our replace expression remains the same.
This one still has problems. What if somebody has put a space before the opening parenthesis, e.g.,
StringBuffer sb = new StringBuffer ();
We can handle this if we want to. Just insert a s* in there, to handle 0 or more whitespace characters. It depends how far you want to go.
Conclusion
Again, for this case, regular expressions are arguably a little bit of overkill. I still like it better because it does skip statements not initializing a StringBuffer, because it matches the whole statement, and if you decide to do the replace, it replaces the whole statement. At the very least, you could end up with one half as many matches that you have to manually review than you would if you did a literal string search and replace. For those of you new to regular expressions, it will hopefully give you a sense of what you can do with them. If you want to start learning more about them, I strongly recommened the previously mentioned book, Mastering Regular Expressions. I had played around with regular expressions before reading the book, but never got the understanding that I did after I read the book.
Charles
I have made some edits since my initial post — I had the usage of StringBuffer and StringBuilder reversed in some cases in the original post.
Share This | Email this page to a friend
Posted by Charles Overbeck on January 16th, 2007 under Java | 8 Comments »Eclipse Update Sites
I’ve been doing some research on preparing the JBuilder 2007 update site. In general, the whole Eclipse update mechanism is pretty slick. If you set it up correctly, the user only has to do Help | Software Updates | Find and Install | Search for updates of the currently installed features, and if you’ve got any updates posted on your web site, the user will get them.
It’s all covered in this article on the Eclipse web site.
Since there is so much to learn about Eclipse, here a few points in this area that are worth emphasizing if you want to deliver updatable plugins to your users. They are discussed in the article, but I’ll highlight them anyway.
- You need to have Eclipse features. I don’t mean feature in the functionality sense, which you must already have or you wouldn’t be developing anything :-). Eclipse features package Eclipse plug-ins. When I first started developing Eclipse plug-ins, the tutorials I followed didn’t discuss features. I assumed that’s all I needed to do. And even today, a lot of the 3rd party Eclipse plugins out there still don’t have features — you just have to manually copy the plugins to your Eclipse installation. At any rate, be sure you understand features before you ship your first plug-in. The update site mechanism does not work with plug-ins that are not packaged by features.
- Your feature.xml should contain the url of your update site. This is not required, but if you don’t do it, then the feature will not be able to automatically update itself. The user will have to go manually enter the url to update your feature. If you forget it, it’s going to make it hard for the user to get updates.
- You need a license. Eclipse won’t let you create features for the update site if they don’t have a license. This is one of those non-technical details that can be pretty important if you are not using some existing OSS license. You may need to even run the license by a lawyer. That takes time (and money!).
Charles
Share This | Email this page to a friend
Posted by Charles Overbeck on January 5th, 2007 under Eclipse | 2 Comments »A couple more essential JBuilder 2007 keystrokes
In a previous blog, I talked about the most common keystrokes I use in day-to-day programming with JBuilder 2007/Eclipse. I’ve got a couple more that I left out. I personally find these extremely useful. You’d think these were all obvious and there’s no need to point them out, but there are so many features, you sometimes don’t know where to start. In fact, my part of the JBuilder team only found about one of these keystrokes, Ctrl+T, when one programmer pressed it by accident — he meant to press Ctrl+Shift+T and discovered it by not simultaneously typing the Shift key.
So, Ctrl+T. Press it when you are positioned on a method declaration or classname. You get a popup that has the inheritance tree for that method or class, and you can arrow through the popup and navigate to any declaration. In JBuilder 2006, you right-click on the the method or class, then do Find References, and then navigate through the Search results. I like the immediacy of the popup in JBuilder 2007 better.
The other one I use all the time is Ctrl+2,L. (You press Ctrl+2, then L, and don’t actually press the comma). It creates a local variable. It changes the way I program. I hardly ever directly type in the declaration of local variables any more. For example, let’s say I want to instantiate a String:
String name = new String("Charles");
Previously, I would have typed in the characters just like you see them above: ‘S’, ‘t’, ‘r’, ‘i’, ‘n’, ‘g’, etc. Now what I do is:
- Type new String("Charles");
- Type Ctrl+2,L
- The line gets changed to:
String string = new String("Charles");with the cursor positioned on string, which I quickly rename to name.
Now, if I want to get the first character of the string and assign it to a variable:
- I type name. and select the charAt function in the Content Assist popup, and specify 0 as the parameter. So I now have:
name.charAt(0);
- I then type good old Ctrl+2, L and my line becomes:
char c = name.charAt(0);
In this case, I don’t even change the name, as I like the proposal.
In fact, in general, the proposals for the names are usually pretty good. Sometimes, based on context they will have multiple choices you can pick from. And the cursor is always positioned on the variable name, so you can change it if you don’t like it.
I had to force myself to change type way I type keys at first. Your initial instinct when you need a local variable is to type in its type and its name. Now I just let JBuilder 2007 figure that out for me. Once you get used to pressing that funky Ctrl+2,L, it will really grow on you.
Well, I’m off on vacation until the beginning of next year, so no more blogs until then. I’m sure you all can hardly wait<g>. When I get back I’ll try to write about something slightly more interesting than keystrokes. But don’t dismiss these keystrokes — they can make you a lot more productive. At least that’s what I tell myself.
Charles
Share This | Email this page to a friend
Posted by Charles Overbeck on December 15th, 2006 under JBuilder | Comment now »Server Response from: dnrh2.codegear.com

RSS Feed