It's a holiday in the US today, so as per tradition, we reach back through the archives. Today is a classic of code generation gone horribly, horribly wrong. Original. --Remy
It’s an old joke that Perl is a “write only language”. Despite some of its issues, back in the early 2000s, Perl was one of the best options out there for scripting languages and rapid-development automation.
Speaking of automation, build automation is really important. Back in the early 2000s, before Maven really caught on, your build automation tool for Java was Ant. Ant, like everything invented in the early 2000s, was driven by an XML scripting tool. Since it was tuned specifically for Java, it had some high-level operations to streamline tasks like generating proxy classes for calling web services based on a supplied WSDL file.
Actually, speaking of code generation, Carrie sends us this code block. It’s a Perl script that’s called from an Ant build. It runs after generating a class based off a WSDL. It parses Java code using Regular Expressions and injects a ListWrapper
class which doesn’t adhere to the List
contract. But hey, it does have a use strict
declaration, guaranteeing you’ll get errors if you access uninitialized variables.
use strict;
use warnings;
my $dir = $ARGV[0];
readDir($dir);
sub readDir {
my ($dir) = @_;
opendir my $dirHandle, $dir or die "Cannot open $dir.\n$!\n";
foreach my $file(readdir($dirHandle)) {
next if $file =~ m/^\./;
if(-d "$dir/$file") {
readDir("$dir/$file");
next;
}
my %seenFields;
my %multiples;
my $fileName = "$dir/$file";
open IN, "<$fileName" or die "Cannot open $fileName.\n$!\n";
my @methods;
my $file = "";
my $currentLine;
my $containsContent = 0;
while(<IN>) {
if($_ =~ m/\@XmlElementRef\(name\s*=\s*"(.*?)".+namespace\s*=\s*"(.*?)".+type\s*=\s*(.*?.class)/) {
my $field = ucfirst($1);
my $namespace = $2;
my $class = $3;
$multiples{$1}++;
if($multiples{$field} > 1) {
$_ =~ s/name\s*=\s*"$1"/name = "$1$multiples{$1}"/gis;
$field = $1 . $multiples{$1};
}
my $fieldlc = lc($field);
my $retObject = substr($class, 0, length($class) - 6);
die if not defined $retObject;
my $methodName = $field;
unless(defined $seenFields{$methodName}) {
$seenFields{$methodName} = 1;
my $method = <<EOF;
public List get$field() {
List all = getContent();
ListWrapper retVal = new ListWrapper(all);
for(java.lang.Object current : all) {
java.lang.String className = null;
if(current instanceof javax.xml.bind.JAXBElement) {
className = ((javax.xml.bind.JAXBElement)current).getName().getLocalPart().toLowerCase();
} else {
className = current.getClass().getSimpleName().toLowerCase();
}
boolean good = false;
if(className.equalsIgnoreCase("$fieldlc")) {
good = true;
} else {
if(className.length() > 4) {
className = className.substring(0, className.length() - 4);
if(className.equalsIgnoreCase("$fieldlc")) {
good = true;
}
}
}
if(good) {
retVal.addWrapped(current);
}
}
return retVal;
}
EOF
push(@methods, $method);
}
} elsif ($_ =~ m/getContent\(\)/) {
$containsContent = 1;
}
$currentLine = $_;
$file .= $currentLine if defined $currentLine;
}
close IN;
if($containsContent) {
print "$fileName\n";
for my $method(@methods) {
$file .= $method;
}
$file .= <<EOF;
private class ListWrapper<T> extends ArrayList<T> {
private List contentsList;
public ListWrapper(List contents) {
super();
contentsList = contents;
}
public boolean addWrapped(T toAdd) {
return super.add(toAdd);
}
\@Override
public boolean add(T toAdd) {
return contentsList.add(toAdd);
}
\@Override
public boolean addAll(java.util.Collection<? extends T> toAdd) {
return contentsList.addAll(toAdd);
}
\@Override
public T set(int index, T element) {
int realIndex = contentsList.indexOf(this.get(index));
return (T)contentsList.set(realIndex, element);
}
}
}
EOF
open OUT, ">$fileName" or die "Cannot open $fileName.\n$!\n";
print OUT $file;
close OUT;
}
}
closedir $dirHandle;
}