Saturday, June 22, 2024

Classic WTF: An Ant Pushes a Perl

Programming LanguageClassic WTF: An Ant Pushes a Perl


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;
}

[Advertisement]
ProGet’s got you covered with security and access controls on your NuGet feeds. Learn more.

Check out our other content

Check out other tags:

Most Popular Articles